1
// Copyright 2019-2022 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
//! Moonbeam Runtime Integration Tests
18

            
19
#![cfg(test)]
20

            
21
mod common;
22
use common::*;
23

            
24
use fp_evm::{Context, IsPrecompileResult};
25
use frame_support::{
26
	assert_noop, assert_ok,
27
	dispatch::DispatchClass,
28
	traits::{
29
		fungible::Inspect, Currency as CurrencyT, EnsureOrigin, PalletInfo, StorageInfo,
30
		StorageInfoTrait,
31
	},
32
	weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight},
33
	StorageHasher, Twox128,
34
};
35
use moonbeam_runtime::currency::{GIGAWEI, WEI};
36
use moonbeam_runtime::{
37
	asset_config::ForeignAssetInstance,
38
	currency::GLMR,
39
	xcm_config::{CurrencyId, SelfReserve},
40
	AccountId, Balances, CrowdloanRewards, Executive, OpenTechCommitteeCollective,
41
	ParachainStaking, PolkadotXcm, Precompiles, Runtime, RuntimeBlockWeights, RuntimeCall,
42
	RuntimeEvent, System, TransactionPayment, TransactionPaymentAsGasPrice,
43
	TreasuryCouncilCollective, XcmTransactor, FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, WEEKS,
44
};
45
use moonbeam_xcm_benchmarks::weights::XcmWeight;
46
use moonkit_xcm_primitives::AccountIdAssetIdConversion;
47
use nimbus_primitives::NimbusId;
48
use pallet_evm::PrecompileSet;
49
use pallet_evm_precompileset_assets_erc20::{SELECTOR_LOG_APPROVAL, SELECTOR_LOG_TRANSFER};
50
use pallet_transaction_payment::Multiplier;
51
use pallet_xcm_transactor::{Currency, CurrencyPayment, TransactWeights};
52
use parity_scale_codec::Encode;
53
use polkadot_parachain::primitives::Sibling;
54
use precompile_utils::{
55
	precompile_set::{is_precompile_or_fail, IsActivePrecompile},
56
	prelude::*,
57
	testing::*,
58
};
59
use sha3::{Digest, Keccak256};
60
use sp_core::{ByteArray, Pair, H160, U256};
61
use sp_runtime::{
62
	traits::{Convert, Dispatchable},
63
	BuildStorage, DispatchError, ModuleError,
64
};
65
use std::str::from_utf8;
66
use xcm::{latest::prelude::*, VersionedAssets, VersionedLocation};
67
use xcm_builder::{ParentIsPreset, SiblingParachainConvertsVia};
68
use xcm_executor::traits::ConvertLocation;
69
use xcm_primitives::split_location_into_chain_part_and_beneficiary;
70

            
71
type BatchPCall = pallet_evm_precompile_batch::BatchPrecompileCall<Runtime>;
72
type CrowdloanRewardsPCall =
73
	pallet_evm_precompile_crowdloan_rewards::CrowdloanRewardsPrecompileCall<Runtime>;
74
type XcmUtilsPCall = pallet_evm_precompile_xcm_utils::XcmUtilsPrecompileCall<
75
	Runtime,
76
	moonbeam_runtime::xcm_config::XcmExecutorConfig,
77
>;
78
type XtokensPCall = pallet_evm_precompile_xtokens::XtokensPrecompileCall<Runtime>;
79
type ForeignAssetsPCall = pallet_evm_precompileset_assets_erc20::Erc20AssetsPrecompileSetCall<
80
	Runtime,
81
	ForeignAssetInstance,
82
>;
83
type XcmTransactorV2PCall =
84
	pallet_evm_precompile_xcm_transactor::v2::XcmTransactorPrecompileV2Call<Runtime>;
85

            
86
const BASE_FEE_GENESIS: u128 = 10000 * GIGAWEI;
87

            
88
3
fn currency_to_asset(currency_id: CurrencyId, amount: u128) -> Asset {
89
3
	Asset {
90
3
		id: AssetId(
91
3
			<moonbeam_runtime::Runtime as pallet_xcm_transactor::Config>::CurrencyIdToLocation::convert(
92
3
				currency_id,
93
3
			)
94
3
			.unwrap(),
95
3
		),
96
3
		fun: Fungibility::Fungible(amount),
97
3
	}
98
3
}
99
#[test]
100
1
fn xcmp_queue_controller_origin_is_root() {
101
1
	// important for the XcmExecutionManager impl of PauseExecution which uses root origin
102
1
	// to suspend/resume XCM execution in xcmp_queue::on_idle
103
1
	assert_ok!(
104
1
		<moonbeam_runtime::Runtime as cumulus_pallet_xcmp_queue::Config
105
1
		>::ControllerOrigin::ensure_origin(root_origin())
106
1
	);
107
1
}
108

            
109
#[test]
110
1
fn verify_pallet_prefixes() {
111
30
	fn is_pallet_prefix<P: 'static>(name: &str) {
112
30
		// Compares the unhashed pallet prefix in the `StorageInstance` implementation by every
113
30
		// storage item in the pallet P. This pallet prefix is used in conjunction with the
114
30
		// item name to get the unique storage key: hash(PalletPrefix) + hash(StorageName)
115
30
		// https://github.com/paritytech/substrate/blob/master/frame/support/procedural/src/pallet/
116
30
		// expand/storage.rs#L389-L401
117
30
		assert_eq!(
118
30
			<moonbeam_runtime::Runtime as frame_system::Config>::PalletInfo::name::<P>(),
119
30
			Some(name)
120
30
		);
121
30
	}
122
1
	// TODO: use StorageInfoTrait once https://github.com/paritytech/substrate/pull/9246
123
1
	// is pulled in substrate deps.
124
1
	is_pallet_prefix::<moonbeam_runtime::System>("System");
125
1
	is_pallet_prefix::<moonbeam_runtime::Utility>("Utility");
126
1
	is_pallet_prefix::<moonbeam_runtime::ParachainSystem>("ParachainSystem");
127
1
	is_pallet_prefix::<moonbeam_runtime::TransactionPayment>("TransactionPayment");
128
1
	is_pallet_prefix::<moonbeam_runtime::ParachainInfo>("ParachainInfo");
129
1
	is_pallet_prefix::<moonbeam_runtime::EthereumChainId>("EthereumChainId");
130
1
	is_pallet_prefix::<moonbeam_runtime::EVM>("EVM");
131
1
	is_pallet_prefix::<moonbeam_runtime::Ethereum>("Ethereum");
132
1
	is_pallet_prefix::<moonbeam_runtime::ParachainStaking>("ParachainStaking");
133
1
	is_pallet_prefix::<moonbeam_runtime::Scheduler>("Scheduler");
134
1
	is_pallet_prefix::<moonbeam_runtime::OpenTechCommitteeCollective>(
135
1
		"OpenTechCommitteeCollective",
136
1
	);
137
1
	is_pallet_prefix::<moonbeam_runtime::Treasury>("Treasury");
138
1
	is_pallet_prefix::<moonbeam_runtime::AuthorInherent>("AuthorInherent");
139
1
	is_pallet_prefix::<moonbeam_runtime::AuthorFilter>("AuthorFilter");
140
1
	is_pallet_prefix::<moonbeam_runtime::CrowdloanRewards>("CrowdloanRewards");
141
1
	is_pallet_prefix::<moonbeam_runtime::AuthorMapping>("AuthorMapping");
142
1
	is_pallet_prefix::<moonbeam_runtime::MaintenanceMode>("MaintenanceMode");
143
1
	is_pallet_prefix::<moonbeam_runtime::Identity>("Identity");
144
1
	is_pallet_prefix::<moonbeam_runtime::XcmpQueue>("XcmpQueue");
145
1
	is_pallet_prefix::<moonbeam_runtime::CumulusXcm>("CumulusXcm");
146
1
	is_pallet_prefix::<moonbeam_runtime::PolkadotXcm>("PolkadotXcm");
147
1
	is_pallet_prefix::<moonbeam_runtime::Assets>("Assets");
148
1
	is_pallet_prefix::<moonbeam_runtime::AssetManager>("AssetManager");
149
1
	is_pallet_prefix::<moonbeam_runtime::Migrations>("Migrations");
150
1
	is_pallet_prefix::<moonbeam_runtime::XcmTransactor>("XcmTransactor");
151
1
	is_pallet_prefix::<moonbeam_runtime::ProxyGenesisCompanion>("ProxyGenesisCompanion");
152
1
	is_pallet_prefix::<moonbeam_runtime::MoonbeamOrbiters>("MoonbeamOrbiters");
153
1
	is_pallet_prefix::<moonbeam_runtime::TreasuryCouncilCollective>("TreasuryCouncilCollective");
154
1
	is_pallet_prefix::<moonbeam_runtime::MoonbeamLazyMigrations>("MoonbeamLazyMigrations");
155
1
	is_pallet_prefix::<moonbeam_runtime::RelayStorageRoots>("RelayStorageRoots");
156
1

            
157
14
	let prefix = |pallet_name, storage_name| {
158
14
		let mut res = [0u8; 32];
159
14
		res[0..16].copy_from_slice(&Twox128::hash(pallet_name));
160
14
		res[16..32].copy_from_slice(&Twox128::hash(storage_name));
161
14
		res.to_vec()
162
14
	};
163
1
	assert_eq!(
164
1
		<moonbeam_runtime::Timestamp as StorageInfoTrait>::storage_info(),
165
1
		vec![
166
1
			StorageInfo {
167
1
				pallet_name: b"Timestamp".to_vec(),
168
1
				storage_name: b"Now".to_vec(),
169
1
				prefix: prefix(b"Timestamp", b"Now"),
170
1
				max_values: Some(1),
171
1
				max_size: Some(8),
172
1
			},
173
1
			StorageInfo {
174
1
				pallet_name: b"Timestamp".to_vec(),
175
1
				storage_name: b"DidUpdate".to_vec(),
176
1
				prefix: prefix(b"Timestamp", b"DidUpdate"),
177
1
				max_values: Some(1),
178
1
				max_size: Some(1),
179
1
			}
180
1
		]
181
1
	);
182
1
	assert_eq!(
183
1
		<moonbeam_runtime::Balances as StorageInfoTrait>::storage_info(),
184
1
		vec![
185
1
			StorageInfo {
186
1
				pallet_name: b"Balances".to_vec(),
187
1
				storage_name: b"TotalIssuance".to_vec(),
188
1
				prefix: prefix(b"Balances", b"TotalIssuance"),
189
1
				max_values: Some(1),
190
1
				max_size: Some(16),
191
1
			},
192
1
			StorageInfo {
193
1
				pallet_name: b"Balances".to_vec(),
194
1
				storage_name: b"InactiveIssuance".to_vec(),
195
1
				prefix: prefix(b"Balances", b"InactiveIssuance"),
196
1
				max_values: Some(1),
197
1
				max_size: Some(16),
198
1
			},
199
1
			StorageInfo {
200
1
				pallet_name: b"Balances".to_vec(),
201
1
				storage_name: b"Account".to_vec(),
202
1
				prefix: prefix(b"Balances", b"Account"),
203
1
				max_values: None,
204
1
				max_size: Some(100),
205
1
			},
206
1
			StorageInfo {
207
1
				pallet_name: b"Balances".to_vec(),
208
1
				storage_name: b"Locks".to_vec(),
209
1
				prefix: prefix(b"Balances", b"Locks"),
210
1
				max_values: None,
211
1
				max_size: Some(1287),
212
1
			},
213
1
			StorageInfo {
214
1
				pallet_name: b"Balances".to_vec(),
215
1
				storage_name: b"Reserves".to_vec(),
216
1
				prefix: prefix(b"Balances", b"Reserves"),
217
1
				max_values: None,
218
1
				max_size: Some(1037),
219
1
			},
220
1
			StorageInfo {
221
1
				pallet_name: b"Balances".to_vec(),
222
1
				storage_name: b"Holds".to_vec(),
223
1
				prefix: prefix(b"Balances", b"Holds"),
224
1
				max_values: None,
225
1
				max_size: Some(55),
226
1
			},
227
1
			StorageInfo {
228
1
				pallet_name: b"Balances".to_vec(),
229
1
				storage_name: b"Freezes".to_vec(),
230
1
				prefix: prefix(b"Balances", b"Freezes"),
231
1
				max_values: None,
232
1
				max_size: Some(37),
233
1
			},
234
1
		]
235
1
	);
236
1
	assert_eq!(
237
1
		<moonbeam_runtime::Proxy as StorageInfoTrait>::storage_info(),
238
1
		vec![
239
1
			StorageInfo {
240
1
				pallet_name: b"Proxy".to_vec(),
241
1
				storage_name: b"Proxies".to_vec(),
242
1
				prefix: prefix(b"Proxy", b"Proxies"),
243
1
				max_values: None,
244
1
				max_size: Some(845),
245
1
			},
246
1
			StorageInfo {
247
1
				pallet_name: b"Proxy".to_vec(),
248
1
				storage_name: b"Announcements".to_vec(),
249
1
				prefix: prefix(b"Proxy", b"Announcements"),
250
1
				max_values: None,
251
1
				max_size: Some(1837),
252
1
			}
253
1
		]
254
1
	);
255
1
	assert_eq!(
256
1
		<moonbeam_runtime::MaintenanceMode as StorageInfoTrait>::storage_info(),
257
1
		vec![StorageInfo {
258
1
			pallet_name: b"MaintenanceMode".to_vec(),
259
1
			storage_name: b"MaintenanceMode".to_vec(),
260
1
			prefix: prefix(b"MaintenanceMode", b"MaintenanceMode"),
261
1
			max_values: Some(1),
262
1
			max_size: None,
263
1
		},]
264
1
	);
265
1
	assert_eq!(
266
1
		<moonbeam_runtime::RelayStorageRoots as StorageInfoTrait>::storage_info(),
267
1
		vec![
268
1
			StorageInfo {
269
1
				pallet_name: b"RelayStorageRoots".to_vec(),
270
1
				storage_name: b"RelayStorageRoot".to_vec(),
271
1
				prefix: prefix(b"RelayStorageRoots", b"RelayStorageRoot"),
272
1
				max_values: None,
273
1
				max_size: Some(44),
274
1
			},
275
1
			StorageInfo {
276
1
				pallet_name: b"RelayStorageRoots".to_vec(),
277
1
				storage_name: b"RelayStorageRootKeys".to_vec(),
278
1
				prefix: prefix(b"RelayStorageRoots", b"RelayStorageRootKeys"),
279
1
				max_values: Some(1),
280
1
				max_size: Some(121),
281
1
			},
282
1
		]
283
1
	);
284
1
}
285

            
286
#[test]
287
1
fn test_collectives_storage_item_prefixes() {
288
6
	for StorageInfo { pallet_name, .. } in
289
7
		<moonbeam_runtime::TreasuryCouncilCollective as StorageInfoTrait>::storage_info()
290
	{
291
6
		assert_eq!(pallet_name, b"TreasuryCouncilCollective".to_vec());
292
	}
293

            
294
6
	for StorageInfo { pallet_name, .. } in
295
7
		<moonbeam_runtime::OpenTechCommitteeCollective as StorageInfoTrait>::storage_info()
296
	{
297
6
		assert_eq!(pallet_name, b"OpenTechCommitteeCollective".to_vec());
298
	}
299
1
}
300

            
301
#[test]
302
1
fn collective_set_members_root_origin_works() {
303
1
	ExtBuilder::default().build().execute_with(|| {
304
1
		// TreasuryCouncilCollective
305
1
		assert_ok!(TreasuryCouncilCollective::set_members(
306
1
			<Runtime as frame_system::Config>::RuntimeOrigin::root(),
307
1
			vec![AccountId::from(ALICE), AccountId::from(BOB)],
308
1
			Some(AccountId::from(ALICE)),
309
1
			2
310
1
		));
311
		// OpenTechCommitteeCollective
312
1
		assert_ok!(OpenTechCommitteeCollective::set_members(
313
1
			<Runtime as frame_system::Config>::RuntimeOrigin::root(),
314
1
			vec![AccountId::from(ALICE), AccountId::from(BOB)],
315
1
			Some(AccountId::from(ALICE)),
316
1
			2
317
1
		));
318
1
	});
319
1
}
320

            
321
#[test]
322
1
fn collective_set_members_general_admin_origin_works() {
323
1
	use moonbeam_runtime::{
324
1
		governance::custom_origins::Origin as CustomOrigin, OriginCaller, Utility,
325
1
	};
326
1

            
327
1
	ExtBuilder::default().build().execute_with(|| {
328
1
		let root_caller = <Runtime as frame_system::Config>::RuntimeOrigin::root();
329
1
		let alice = AccountId::from(ALICE);
330
1

            
331
1
		// TreasuryCouncilCollective
332
1
		let _ = Utility::dispatch_as(
333
1
			root_caller.clone(),
334
1
			Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)),
335
1
			Box::new(
336
1
				pallet_collective::Call::<Runtime, pallet_collective::Instance3>::set_members {
337
1
					new_members: vec![alice, AccountId::from(BOB)],
338
1
					prime: Some(alice),
339
1
					old_count: 2,
340
1
				}
341
1
				.into(),
342
1
			),
343
1
		);
344
1
		// OpenTechCommitteeCollective
345
1
		let _ = Utility::dispatch_as(
346
1
			root_caller,
347
1
			Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)),
348
1
			Box::new(
349
1
				pallet_collective::Call::<Runtime, pallet_collective::Instance4>::set_members {
350
1
					new_members: vec![alice, AccountId::from(BOB)],
351
1
					prime: Some(alice),
352
1
					old_count: 2,
353
1
				}
354
1
				.into(),
355
1
			),
356
1
		);
357
1

            
358
1
		assert_eq!(
359
1
			System::events()
360
1
				.into_iter()
361
2
				.filter_map(|r| {
362
2
					match r.event {
363
2
						RuntimeEvent::Utility(pallet_utility::Event::DispatchedAs { result })
364
2
							if result.is_ok() =>
365
2
						{
366
2
							Some(true)
367
						}
368
						_ => None,
369
					}
370
2
				})
371
1
				.collect::<Vec<_>>()
372
1
				.len(),
373
1
			2
374
1
		)
375
1
	});
376
1
}
377

            
378
#[test]
379
1
fn collective_set_members_signed_origin_does_not_work() {
380
1
	let alice = AccountId::from(ALICE);
381
1
	ExtBuilder::default().build().execute_with(|| {
382
1
		// TreasuryCouncilCollective
383
1
		assert!(TreasuryCouncilCollective::set_members(
384
1
			<Runtime as frame_system::Config>::RuntimeOrigin::signed(alice),
385
1
			vec![AccountId::from(ALICE), AccountId::from(BOB)],
386
1
			Some(AccountId::from(ALICE)),
387
1
			2
388
1
		)
389
1
		.is_err());
390
		// OpenTechCommitteeCollective
391
1
		assert!(OpenTechCommitteeCollective::set_members(
392
1
			<Runtime as frame_system::Config>::RuntimeOrigin::signed(alice),
393
1
			vec![AccountId::from(ALICE), AccountId::from(BOB)],
394
1
			Some(AccountId::from(ALICE)),
395
1
			2
396
1
		)
397
1
		.is_err());
398
1
	});
399
1
}
400

            
401
#[test]
402
1
fn verify_pallet_indices() {
403
32
	fn is_pallet_index<P: 'static>(index: usize) {
404
32
		assert_eq!(
405
32
			<moonbeam_runtime::Runtime as frame_system::Config>::PalletInfo::index::<P>(),
406
32
			Some(index)
407
32
		);
408
32
	}
409
1

            
410
1
	// System support
411
1
	is_pallet_index::<moonbeam_runtime::System>(0);
412
1
	is_pallet_index::<moonbeam_runtime::ParachainSystem>(1);
413
1
	is_pallet_index::<moonbeam_runtime::Timestamp>(3);
414
1
	is_pallet_index::<moonbeam_runtime::ParachainInfo>(4);
415
1
	// Monetary
416
1
	is_pallet_index::<moonbeam_runtime::Balances>(10);
417
1
	is_pallet_index::<moonbeam_runtime::TransactionPayment>(11);
418
1
	// Consensus support
419
1
	is_pallet_index::<moonbeam_runtime::ParachainStaking>(20);
420
1
	is_pallet_index::<moonbeam_runtime::AuthorInherent>(21);
421
1
	is_pallet_index::<moonbeam_runtime::AuthorFilter>(22);
422
1
	is_pallet_index::<moonbeam_runtime::AuthorMapping>(23);
423
1
	is_pallet_index::<moonbeam_runtime::MoonbeamOrbiters>(24);
424
1
	// Handy utilities
425
1
	is_pallet_index::<moonbeam_runtime::Utility>(30);
426
1
	is_pallet_index::<moonbeam_runtime::Proxy>(31);
427
1
	is_pallet_index::<moonbeam_runtime::MaintenanceMode>(32);
428
1
	is_pallet_index::<moonbeam_runtime::Identity>(33);
429
1
	is_pallet_index::<moonbeam_runtime::Migrations>(34);
430
1
	is_pallet_index::<moonbeam_runtime::ProxyGenesisCompanion>(35);
431
1
	is_pallet_index::<moonbeam_runtime::MoonbeamLazyMigrations>(37);
432
1
	// Ethereum compatibility
433
1
	is_pallet_index::<moonbeam_runtime::EthereumChainId>(50);
434
1
	is_pallet_index::<moonbeam_runtime::EVM>(51);
435
1
	is_pallet_index::<moonbeam_runtime::Ethereum>(52);
436
1
	// Governance
437
1
	is_pallet_index::<moonbeam_runtime::Scheduler>(60);
438
1
	// is_pallet_index::<moonbeam_runtime::Democracy>(61); Removed
439
1
	// Council
440
1
	// is_pallet_index::<moonbeam_runtime::CouncilCollective>(70); Removed
441
1
	// is_pallet_index::<moonbeam_runtime::TechCommitteeCollective>(71); Removed
442
1
	is_pallet_index::<moonbeam_runtime::TreasuryCouncilCollective>(72);
443
1
	is_pallet_index::<moonbeam_runtime::OpenTechCommitteeCollective>(73);
444
1
	// Treasury
445
1
	is_pallet_index::<moonbeam_runtime::Treasury>(80);
446
1
	// Crowdloan
447
1
	is_pallet_index::<moonbeam_runtime::CrowdloanRewards>(90);
448
1
	// XCM Stuff
449
1
	is_pallet_index::<moonbeam_runtime::XcmpQueue>(100);
450
1
	is_pallet_index::<moonbeam_runtime::CumulusXcm>(101);
451
1
	is_pallet_index::<moonbeam_runtime::PolkadotXcm>(103);
452
1
	is_pallet_index::<moonbeam_runtime::Assets>(104);
453
1
	is_pallet_index::<moonbeam_runtime::AssetManager>(105);
454
1
	// is_pallet_index::<moonbeam_runtime::XTokens>(106); Removed
455
1
	is_pallet_index::<moonbeam_runtime::XcmTransactor>(107);
456
1
}
457

            
458
#[test]
459
1
fn verify_reserved_indices() {
460
1
	let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
461
1
		.build_storage()
462
1
		.unwrap()
463
1
		.into();
464
1

            
465
1
	t.execute_with(|| {
466
1
		use frame_metadata::*;
467
1
		let metadata = moonbeam_runtime::Runtime::metadata();
468
1
		let metadata = match metadata.1 {
469
1
			RuntimeMetadata::V14(metadata) => metadata,
470
			_ => panic!("metadata has been bumped, test needs to be updated"),
471
		};
472
		// 40: Sudo
473
		// 53: BaseFee
474
		// 108: pallet_assets::<Instance1>
475
1
		let reserved = vec![40, 53, 108];
476
1
		let existing = metadata
477
1
			.pallets
478
1
			.iter()
479
49
			.map(|p| p.index)
480
1
			.collect::<Vec<u8>>();
481
3
		assert!(reserved.iter().all(|index| !existing.contains(index)));
482
1
	});
483
1
}
484

            
485
#[test]
486
1
fn verify_proxy_type_indices() {
487
1
	assert_eq!(moonbeam_runtime::ProxyType::Any as u8, 0);
488
1
	assert_eq!(moonbeam_runtime::ProxyType::NonTransfer as u8, 1);
489
1
	assert_eq!(moonbeam_runtime::ProxyType::Governance as u8, 2);
490
1
	assert_eq!(moonbeam_runtime::ProxyType::Staking as u8, 3);
491
1
	assert_eq!(moonbeam_runtime::ProxyType::CancelProxy as u8, 4);
492
1
	assert_eq!(moonbeam_runtime::ProxyType::Balances as u8, 5);
493
1
	assert_eq!(moonbeam_runtime::ProxyType::AuthorMapping as u8, 6);
494
1
	assert_eq!(moonbeam_runtime::ProxyType::IdentityJudgement as u8, 7);
495
1
}
496

            
497
#[test]
498
1
fn join_collator_candidates() {
499
1
	ExtBuilder::default()
500
1
		.with_balances(vec![
501
1
			(AccountId::from(ALICE), 10_000_000 * GLMR),
502
1
			(AccountId::from(BOB), 10_000_000 * GLMR),
503
1
			(AccountId::from(CHARLIE), 10_000_000 * GLMR),
504
1
			(AccountId::from(DAVE), 10_000_000 * GLMR),
505
1
		])
506
1
		.with_collators(vec![
507
1
			(AccountId::from(ALICE), 2_000_000 * GLMR),
508
1
			(AccountId::from(BOB), 2_000_000 * GLMR),
509
1
		])
510
1
		.with_delegations(vec![
511
1
			(
512
1
				AccountId::from(CHARLIE),
513
1
				AccountId::from(ALICE),
514
1
				5_000 * GLMR,
515
1
			),
516
1
			(AccountId::from(CHARLIE), AccountId::from(BOB), 5_000 * GLMR),
517
1
		])
518
1
		.build()
519
1
		.execute_with(|| {
520
1
			assert_noop!(
521
1
				ParachainStaking::join_candidates(
522
1
					origin_of(AccountId::from(ALICE)),
523
1
					2_000_000 * GLMR,
524
1
					2u32
525
1
				),
526
1
				pallet_parachain_staking::Error::<Runtime>::CandidateExists
527
1
			);
528
1
			assert_noop!(
529
1
				ParachainStaking::join_candidates(
530
1
					origin_of(AccountId::from(CHARLIE)),
531
1
					2_000_000 * GLMR,
532
1
					2u32
533
1
				),
534
1
				pallet_parachain_staking::Error::<Runtime>::DelegatorExists
535
1
			);
536
1
			assert!(System::events().is_empty());
537
1
			assert_ok!(ParachainStaking::join_candidates(
538
1
				origin_of(AccountId::from(DAVE)),
539
1
				2_000_000 * GLMR,
540
1
				2u32
541
1
			));
542
1
			assert_eq!(
543
1
				last_event(),
544
1
				RuntimeEvent::ParachainStaking(
545
1
					pallet_parachain_staking::Event::JoinedCollatorCandidates {
546
1
						account: AccountId::from(DAVE),
547
1
						amount_locked: 2_000_000 * GLMR,
548
1
						new_total_amt_locked: 6_010_000 * GLMR
549
1
					}
550
1
				)
551
1
			);
552
1
			let candidates = ParachainStaking::candidate_pool();
553
1
			assert_eq!(candidates.0[0].owner, AccountId::from(ALICE));
554
1
			assert_eq!(candidates.0[0].amount, 2_005_000 * GLMR);
555
1
			assert_eq!(candidates.0[1].owner, AccountId::from(BOB));
556
1
			assert_eq!(candidates.0[1].amount, 2_005_000 * GLMR);
557
1
			assert_eq!(candidates.0[2].owner, AccountId::from(DAVE));
558
1
			assert_eq!(candidates.0[2].amount, 2_000_000 * GLMR);
559
1
		});
560
1
}
561

            
562
#[test]
563
1
fn transfer_through_evm_to_stake() {
564
1
	ExtBuilder::default()
565
1
		.with_balances(vec![(AccountId::from(ALICE), 10_000_000 * GLMR)])
566
1
		.build()
567
1
		.execute_with(|| {
568
1
			// Charlie has no balance => fails to stake
569
1
			assert_noop!(
570
1
				ParachainStaking::join_candidates(
571
1
					origin_of(AccountId::from(CHARLIE)),
572
1
					2_000_000 * GLMR,
573
1
					2u32
574
1
				),
575
1
				DispatchError::Module(ModuleError {
576
1
					index: 20,
577
1
					error: [8, 0, 0, 0],
578
1
					message: Some("InsufficientBalance")
579
1
				})
580
1
			);
581
			// Alice transfer from free balance 3_000_000 GLMR to Bob
582
1
			assert_ok!(Balances::transfer_allow_death(
583
1
				origin_of(AccountId::from(ALICE)),
584
1
				AccountId::from(BOB),
585
1
				3_000_000 * GLMR,
586
1
			));
587
1
			assert_eq!(
588
1
				Balances::free_balance(AccountId::from(BOB)),
589
1
				3_000_000 * GLMR
590
1
			);
591

            
592
1
			let gas_limit = 100000u64;
593
1
			let gas_price: U256 = BASE_FEE_GENESIS.into();
594
1
			// Bob transfers 2_000_000 GLMR to Charlie via EVM
595
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
596
1
				source: H160::from(BOB),
597
1
				target: H160::from(CHARLIE),
598
1
				input: vec![],
599
1
				value: (2_000_000 * GLMR).into(),
600
1
				gas_limit,
601
1
				max_fee_per_gas: gas_price,
602
1
				max_priority_fee_per_gas: None,
603
1
				nonce: None,
604
1
				access_list: Vec::new(),
605
1
			})
606
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
607
1
			assert_eq!(
608
1
				Balances::free_balance(AccountId::from(CHARLIE)),
609
1
				2_000_000 * GLMR,
610
1
			);
611

            
612
			// Charlie can stake now
613
1
			assert_ok!(ParachainStaking::join_candidates(
614
1
				origin_of(AccountId::from(CHARLIE)),
615
1
				2_000_000 * GLMR,
616
1
				2u32
617
1
			),);
618
1
			let candidates = ParachainStaking::candidate_pool();
619
1
			assert_eq!(candidates.0[0].owner, AccountId::from(CHARLIE));
620
1
			assert_eq!(candidates.0[0].amount, 2_000_000 * GLMR);
621
1
		});
622
1
}
623

            
624
#[test]
625
1
fn reward_block_authors() {
626
1
	ExtBuilder::default()
627
1
		.with_balances(vec![
628
1
			// Alice gets 10k extra tokens for her mapping deposit
629
1
			(AccountId::from(ALICE), 10_010_000 * GLMR),
630
1
			(AccountId::from(BOB), 10_000_000 * GLMR),
631
1
		])
632
1
		.with_collators(vec![(AccountId::from(ALICE), 2_000_000 * GLMR)])
633
1
		.with_delegations(vec![(
634
1
			AccountId::from(BOB),
635
1
			AccountId::from(ALICE),
636
1
			50_000 * GLMR,
637
1
		)])
638
1
		.with_mappings(vec![(
639
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
640
1
			AccountId::from(ALICE),
641
1
		)])
642
1
		.build()
643
1
		.execute_with(|| {
644
1
			increase_last_relay_slot_number(1);
645
1
			// Just before round 3
646
1
			run_to_block(7199, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
647
1
			// no rewards doled out yet
648
1
			assert_eq!(
649
1
				Balances::usable_balance(AccountId::from(ALICE)),
650
1
				8_010_000 * GLMR,
651
1
			);
652
1
			assert_eq!(
653
1
				Balances::usable_balance(AccountId::from(BOB)),
654
1
				9_950_000 * GLMR,
655
1
			);
656
1
			run_to_block(7201, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
657
1
			// rewards minted and distributed
658
1
			assert_eq!(
659
1
				Balances::usable_balance(AccountId::from(ALICE)),
660
1
				8990978048702400000000000,
661
1
			);
662
1
			assert_eq!(
663
1
				Balances::usable_balance(AccountId::from(BOB)),
664
1
				9969521950497200000000000,
665
1
			);
666
1
		});
667
1
}
668

            
669
#[test]
670
1
fn reward_block_authors_with_parachain_bond_reserved() {
671
1
	ExtBuilder::default()
672
1
		.with_balances(vec![
673
1
			// Alice gets 10k extra tokens for her mapping deposit
674
1
			(AccountId::from(ALICE), 10_010_000 * GLMR),
675
1
			(AccountId::from(BOB), 10_000_000 * GLMR),
676
1
			(AccountId::from(CHARLIE), 10_000 * GLMR),
677
1
		])
678
1
		.with_collators(vec![(AccountId::from(ALICE), 2_000_000 * GLMR)])
679
1
		.with_delegations(vec![(
680
1
			AccountId::from(BOB),
681
1
			AccountId::from(ALICE),
682
1
			50_000 * GLMR,
683
1
		)])
684
1
		.with_mappings(vec![(
685
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
686
1
			AccountId::from(ALICE),
687
1
		)])
688
1
		.build()
689
1
		.execute_with(|| {
690
1
			increase_last_relay_slot_number(1);
691
1
			assert_ok!(ParachainStaking::set_parachain_bond_account(
692
1
				root_origin(),
693
1
				AccountId::from(CHARLIE),
694
1
			),);
695

            
696
			// Stop just before round 3
697
1
			run_to_block(7199, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
698
1
			// no collators rewards doled out yet
699
1
			assert_eq!(
700
1
				Balances::usable_balance(AccountId::from(ALICE)),
701
1
				8_010_000 * GLMR,
702
1
			);
703
1
			assert_eq!(
704
1
				Balances::usable_balance(AccountId::from(BOB)),
705
1
				9_950_000 * GLMR,
706
1
			);
707
			// 30% reserved for parachain bond
708
1
			assert_eq!(
709
1
				Balances::usable_balance(AccountId::from(CHARLIE)),
710
1
				310300000000000000000000,
711
1
			);
712

            
713
			// Go to round 3
714
1
			run_to_block(7201, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
715
1

            
716
1
			// collators rewards minted and distributed
717
1
			assert_eq!(
718
1
				Balances::usable_balance(AccountId::from(ALICE)),
719
1
				8698492682878000000000000,
720
1
			);
721
1
			assert_eq!(
722
1
				Balances::usable_balance(AccountId::from(BOB)),
723
1
				9962207316621500000000000,
724
1
			);
725
			// 30% reserved for parachain bond again
726
1
			assert_eq!(
727
1
				Balances::usable_balance(AccountId::from(CHARLIE)),
728
1
				615104500000000000000000,
729
1
			);
730
1
		});
731
1
}
732

            
733
#[test]
734
1
fn initialize_crowdloan_addresses_with_batch_and_pay() {
735
1
	ExtBuilder::default()
736
1
		.with_balances(vec![
737
1
			(AccountId::from(ALICE), 200_000 * GLMR),
738
1
			(AccountId::from(BOB), 100_000 * GLMR),
739
1
		])
740
1
		.with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)])
741
1
		.with_mappings(vec![(
742
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
743
1
			AccountId::from(ALICE),
744
1
		)])
745
1
		.with_crowdloan_fund(300_000_000 * GLMR)
746
1
		.build()
747
1
		.execute_with(|| {
748
1
			// set parachain inherent data
749
1
			set_parachain_inherent_data();
750
1
			let init_block = CrowdloanRewards::init_vesting_block();
751
1
			// This matches the previous vesting
752
1
			let end_block = init_block + 4 * WEEKS;
753
1
			// Batch calls always succeed. We just need to check the inner event
754
1
			assert_ok!(
755
1
				RuntimeCall::Utility(pallet_utility::Call::<Runtime>::batch_all {
756
1
					calls: vec![
757
1
						RuntimeCall::CrowdloanRewards(
758
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
759
1
								rewards: vec![(
760
1
									[4u8; 32].into(),
761
1
									Some(AccountId::from(CHARLIE)),
762
1
									150_000_000 * GLMR
763
1
								)]
764
1
							}
765
1
						),
766
1
						RuntimeCall::CrowdloanRewards(
767
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
768
1
								rewards: vec![(
769
1
									[5u8; 32].into(),
770
1
									Some(AccountId::from(DAVE)),
771
1
									150_000_000 * GLMR
772
1
								)]
773
1
							}
774
1
						),
775
1
						RuntimeCall::CrowdloanRewards(
776
1
							pallet_crowdloan_rewards::Call::<Runtime>::complete_initialization {
777
1
								lease_ending_block: end_block
778
1
							}
779
1
						)
780
1
					]
781
1
				})
782
1
				.dispatch(root_origin())
783
1
			);
784
			// 30 percent initial payout
785
1
			assert_eq!(
786
1
				Balances::balance(&AccountId::from(CHARLIE)),
787
1
				45_000_000 * GLMR
788
1
			);
789
			// 30 percent initial payout
790
1
			assert_eq!(Balances::balance(&AccountId::from(DAVE)), 45_000_000 * GLMR);
791
1
			let expected = RuntimeEvent::Utility(pallet_utility::Event::BatchCompleted);
792
1
			assert_eq!(last_event(), expected);
793
			// This one should fail, as we already filled our data
794
1
			assert_ok!(
795
1
				RuntimeCall::Utility(pallet_utility::Call::<Runtime>::batch {
796
1
					calls: vec![RuntimeCall::CrowdloanRewards(
797
1
						pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
798
1
							rewards: vec![(
799
1
								[4u8; 32].into(),
800
1
								Some(AccountId::from(ALICE)),
801
1
								43_200_000
802
1
							)]
803
1
						}
804
1
					)]
805
1
				})
806
1
				.dispatch(root_origin())
807
1
			);
808
1
			let expected_fail = RuntimeEvent::Utility(pallet_utility::Event::BatchInterrupted {
809
1
				index: 0,
810
1
				error: DispatchError::Module(ModuleError {
811
1
					index: 90,
812
1
					error: [8, 0, 0, 0],
813
1
					message: None,
814
1
				}),
815
1
			});
816
1
			assert_eq!(last_event(), expected_fail);
817
			// Claim 1 block.
818
1
			assert_ok!(CrowdloanRewards::claim(origin_of(AccountId::from(CHARLIE))));
819
1
			assert_ok!(CrowdloanRewards::claim(origin_of(AccountId::from(DAVE))));
820

            
821
1
			let vesting_period = 4 * WEEKS as u128;
822
1
			let per_block = (105_000_000 * GLMR) / vesting_period;
823
1

            
824
1
			assert_eq!(
825
1
				CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE))
826
1
					.unwrap()
827
1
					.claimed_reward,
828
1
				(45_000_000 * GLMR) + per_block
829
1
			);
830
1
			assert_eq!(
831
1
				CrowdloanRewards::accounts_payable(&AccountId::from(DAVE))
832
1
					.unwrap()
833
1
					.claimed_reward,
834
1
				(45_000_000 * GLMR) + per_block
835
1
			);
836
			// The total claimed reward should be equal to the account balance at this point.
837
1
			assert_eq!(
838
1
				Balances::balance(&AccountId::from(CHARLIE)),
839
1
				(45_000_000 * GLMR) + per_block
840
1
			);
841
1
			assert_eq!(
842
1
				Balances::balance(&AccountId::from(DAVE)),
843
1
				(45_000_000 * GLMR) + per_block
844
1
			);
845
1
			assert_noop!(
846
1
				CrowdloanRewards::claim(origin_of(AccountId::from(ALICE))),
847
1
				pallet_crowdloan_rewards::Error::<Runtime>::NoAssociatedClaim
848
1
			);
849
1
		});
850
1
}
851

            
852
#[test]
853
1
fn initialize_crowdloan_address_and_change_with_relay_key_sig() {
854
1
	ExtBuilder::default()
855
1
		.with_balances(vec![
856
1
			(AccountId::from(ALICE), 2_000 * GLMR),
857
1
			(AccountId::from(BOB), 1_000 * GLMR),
858
1
		])
859
1
		.with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)])
860
1
		.with_mappings(vec![(
861
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
862
1
			AccountId::from(ALICE),
863
1
		)])
864
1
		.with_crowdloan_fund(3_000_000 * GLMR)
865
1
		.build()
866
1
		.execute_with(|| {
867
1
			// set parachain inherent data
868
1
			set_parachain_inherent_data();
869
1
			let init_block = CrowdloanRewards::init_vesting_block();
870
1
			// This matches the previous vesting
871
1
			let end_block = init_block + 4 * WEEKS;
872
1

            
873
1
			let (pair1, _) = sp_core::sr25519::Pair::generate();
874
1
			let (pair2, _) = sp_core::sr25519::Pair::generate();
875
1

            
876
1
			let public1 = pair1.public();
877
1
			let public2 = pair2.public();
878
1

            
879
1
			// signature is new_account || previous_account
880
1
			let mut message = pallet_crowdloan_rewards::WRAPPED_BYTES_PREFIX.to_vec();
881
1
			message.append(&mut b"moonbeam-".to_vec());
882
1
			message.append(&mut AccountId::from(DAVE).encode());
883
1
			message.append(&mut AccountId::from(CHARLIE).encode());
884
1
			message.append(&mut pallet_crowdloan_rewards::WRAPPED_BYTES_POSTFIX.to_vec());
885
1
			let signature1 = pair1.sign(&message);
886
1
			let signature2 = pair2.sign(&message);
887
1

            
888
1
			// Batch calls always succeed. We just need to check the inner event
889
1
			assert_ok!(
890
1
				// two relay accounts pointing at the same reward account
891
1
				RuntimeCall::Utility(pallet_utility::Call::<Runtime>::batch_all {
892
1
					calls: vec![
893
1
						RuntimeCall::CrowdloanRewards(
894
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
895
1
								rewards: vec![(
896
1
									public1.into(),
897
1
									Some(AccountId::from(CHARLIE)),
898
1
									1_500_000 * GLMR
899
1
								)]
900
1
							}
901
1
						),
902
1
						RuntimeCall::CrowdloanRewards(
903
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
904
1
								rewards: vec![(
905
1
									public2.into(),
906
1
									Some(AccountId::from(CHARLIE)),
907
1
									1_500_000 * GLMR
908
1
								)]
909
1
							}
910
1
						),
911
1
						RuntimeCall::CrowdloanRewards(
912
1
							pallet_crowdloan_rewards::Call::<Runtime>::complete_initialization {
913
1
								lease_ending_block: end_block
914
1
							}
915
1
						)
916
1
					]
917
1
				})
918
1
				.dispatch(root_origin())
919
1
			);
920
			// 30 percent initial payout
921
1
			assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 900_000 * GLMR);
922

            
923
			// this should fail, as we are only providing one signature
924
1
			assert_noop!(
925
1
				CrowdloanRewards::change_association_with_relay_keys(
926
1
					origin_of(AccountId::from(CHARLIE)),
927
1
					AccountId::from(DAVE),
928
1
					AccountId::from(CHARLIE),
929
1
					vec![(public1.into(), signature1.clone().into())]
930
1
				),
931
1
				pallet_crowdloan_rewards::Error::<Runtime>::InsufficientNumberOfValidProofs
932
1
			);
933

            
934
			// this should be valid
935
1
			assert_ok!(CrowdloanRewards::change_association_with_relay_keys(
936
1
				origin_of(AccountId::from(CHARLIE)),
937
1
				AccountId::from(DAVE),
938
1
				AccountId::from(CHARLIE),
939
1
				vec![
940
1
					(public1.into(), signature1.into()),
941
1
					(public2.into(), signature2.into())
942
1
				]
943
1
			));
944

            
945
1
			assert_eq!(
946
1
				CrowdloanRewards::accounts_payable(&AccountId::from(DAVE))
947
1
					.unwrap()
948
1
					.claimed_reward,
949
1
				(900_000 * GLMR)
950
1
			);
951
1
		});
952
1
}
953

            
954
#[test]
955
1
fn claim_via_precompile() {
956
1
	ExtBuilder::default()
957
1
		.with_balances(vec![
958
1
			(AccountId::from(ALICE), 2_000 * GLMR),
959
1
			(AccountId::from(BOB), 1_000 * GLMR),
960
1
		])
961
1
		.with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)])
962
1
		.with_mappings(vec![(
963
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
964
1
			AccountId::from(ALICE),
965
1
		)])
966
1
		.with_crowdloan_fund(3_000_000 * GLMR)
967
1
		.build()
968
1
		.execute_with(|| {
969
1
			// set parachain inherent data
970
1
			set_parachain_inherent_data();
971
1
			let init_block = CrowdloanRewards::init_vesting_block();
972
1
			// This matches the previous vesting
973
1
			let end_block = init_block + 4 * WEEKS;
974
1
			// Batch calls always succeed. We just need to check the inner event
975
1
			assert_ok!(
976
1
				RuntimeCall::Utility(pallet_utility::Call::<Runtime>::batch_all {
977
1
					calls: vec![
978
1
						RuntimeCall::CrowdloanRewards(
979
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
980
1
								rewards: vec![(
981
1
									[4u8; 32].into(),
982
1
									Some(AccountId::from(CHARLIE)),
983
1
									1_500_000 * GLMR
984
1
								)]
985
1
							}
986
1
						),
987
1
						RuntimeCall::CrowdloanRewards(
988
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
989
1
								rewards: vec![(
990
1
									[5u8; 32].into(),
991
1
									Some(AccountId::from(DAVE)),
992
1
									1_500_000 * GLMR
993
1
								)]
994
1
							}
995
1
						),
996
1
						RuntimeCall::CrowdloanRewards(
997
1
							pallet_crowdloan_rewards::Call::<Runtime>::complete_initialization {
998
1
								lease_ending_block: end_block
999
1
							}
1
						)
1
					]
1
				})
1
				.dispatch(root_origin())
1
			);
1
			assert!(CrowdloanRewards::initialized());
			// 30 percent initial payout
1
			assert_eq!(Balances::balance(&AccountId::from(CHARLIE)), 450_000 * GLMR);
			// 30 percent initial payout
1
			assert_eq!(Balances::balance(&AccountId::from(DAVE)), 450_000 * GLMR);
1
			let crowdloan_precompile_address = H160::from_low_u64_be(2049);
1

            
1
			// Alice uses the crowdloan precompile to claim through the EVM
1
			let gas_limit = 100000u64;
1
			let gas_price: U256 = BASE_FEE_GENESIS.into();
1

            
1
			// Construct the call data (selector, amount)
1
			let mut call_data = Vec::<u8>::from([0u8; 4]);
1
			call_data[0..4].copy_from_slice(&Keccak256::digest(b"claim()")[0..4]);
1

            
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
1
				source: H160::from(CHARLIE),
1
				target: crowdloan_precompile_address,
1
				input: call_data,
1
				value: U256::zero(), // No value sent in EVM
1
				gas_limit,
1
				max_fee_per_gas: gas_price,
1
				max_priority_fee_per_gas: None,
1
				nonce: None, // Use the next nonce
1
				access_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
1
			let vesting_period = 4 * WEEKS as u128;
1
			let per_block = (1_050_000 * GLMR) / vesting_period;
1

            
1
			assert_eq!(
1
				CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE))
1
					.unwrap()
1
					.claimed_reward,
1
				(450_000 * GLMR) + per_block
1
			);
1
		})
1
}
#[test]
1
fn is_contributor_via_precompile() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 200_000 * GLMR),
1
			(AccountId::from(BOB), 100_000 * GLMR),
1
		])
1
		.with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)])
1
		.with_mappings(vec![(
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
1
			AccountId::from(ALICE),
1
		)])
1
		.with_crowdloan_fund(3_000_000_000 * GLMR)
1
		.build()
1
		.execute_with(|| {
1
			// set parachain inherent data
1
			set_parachain_inherent_data();
1
			let init_block = CrowdloanRewards::init_vesting_block();
1
			// This matches the previous vesting
1
			let end_block = init_block + 4 * WEEKS;
1
			// Batch calls always succeed. We just need to check the inner event
1
			assert_ok!(
1
				RuntimeCall::Utility(pallet_utility::Call::<Runtime>::batch_all {
1
					calls: vec![
1
						RuntimeCall::CrowdloanRewards(
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
1
								rewards: vec![(
1
									[4u8; 32].into(),
1
									Some(AccountId::from(CHARLIE)),
1
									1_500_000_000 * GLMR
1
								)]
1
							}
1
						),
1
						RuntimeCall::CrowdloanRewards(
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
1
								rewards: vec![(
1
									[5u8; 32].into(),
1
									Some(AccountId::from(DAVE)),
1
									1_500_000_000 * GLMR
1
								)]
1
							}
1
						),
1
						RuntimeCall::CrowdloanRewards(
1
							pallet_crowdloan_rewards::Call::<Runtime>::complete_initialization {
1
								lease_ending_block: end_block
1
							}
1
						)
1
					]
1
				})
1
				.dispatch(root_origin())
1
			);
1
			let crowdloan_precompile_address = H160::from_low_u64_be(2049);
1

            
1
			// Assert precompile reports Bob is not a contributor
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					crowdloan_precompile_address,
1
					CrowdloanRewardsPCall::is_contributor {
1
						contributor: Address(AccountId::from(BOB).into()),
1
					},
1
				)
1
				.expect_cost(1669)
1
				.expect_no_logs()
1
				.execute_returns(false);
1

            
1
			// Assert precompile reports Charlie is a nominator
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					crowdloan_precompile_address,
1
					CrowdloanRewardsPCall::is_contributor {
1
						contributor: Address(AccountId::from(CHARLIE).into()),
1
					},
1
				)
1
				.expect_cost(1669)
1
				.expect_no_logs()
1
				.execute_returns(true);
1
		})
1
}
#[test]
1
fn reward_info_via_precompile() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 200_000 * GLMR),
1
			(AccountId::from(BOB), 100_000 * GLMR),
1
		])
1
		.with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)])
1
		.with_mappings(vec![(
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
1
			AccountId::from(ALICE),
1
		)])
1
		.with_crowdloan_fund(3_000_000 * GLMR)
1
		.build()
1
		.execute_with(|| {
1
			// set parachain inherent data
1
			set_parachain_inherent_data();
1
			let init_block = CrowdloanRewards::init_vesting_block();
1
			// This matches the previous vesting
1
			let end_block = init_block + 4 * WEEKS;
1
			// Batch calls always succeed. We just need to check the inner event
1
			assert_ok!(
1
				RuntimeCall::Utility(pallet_utility::Call::<Runtime>::batch_all {
1
					calls: vec![
1
						RuntimeCall::CrowdloanRewards(
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
1
								rewards: vec![(
1
									[4u8; 32].into(),
1
									Some(AccountId::from(CHARLIE)),
1
									1_500_000 * GLMR
1
								)]
1
							}
1
						),
1
						RuntimeCall::CrowdloanRewards(
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
1
								rewards: vec![(
1
									[5u8; 32].into(),
1
									Some(AccountId::from(DAVE)),
1
									1_500_000 * GLMR
1
								)]
1
							}
1
						),
1
						RuntimeCall::CrowdloanRewards(
1
							pallet_crowdloan_rewards::Call::<Runtime>::complete_initialization {
1
								lease_ending_block: end_block
1
							}
1
						)
1
					]
1
				})
1
				.dispatch(root_origin())
1
			);
1
			let crowdloan_precompile_address = H160::from_low_u64_be(2049);
1

            
1
			let expected_total: U256 = (1_500_000 * GLMR).into();
1
			let expected_claimed: U256 = (450_000 * GLMR).into();
1

            
1
			// Assert precompile reports correct Charlie reward info.
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					crowdloan_precompile_address,
1
					CrowdloanRewardsPCall::reward_info {
1
						contributor: Address(AccountId::from(CHARLIE).into()),
1
					},
1
				)
1
				.expect_cost(1669)
1
				.expect_no_logs()
1
				.execute_returns((expected_total, expected_claimed));
1
		})
1
}
#[test]
1
fn update_reward_address_via_precompile() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)])
1
		.with_mappings(vec![(
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
1
			AccountId::from(ALICE),
1
		)])
1
		.with_crowdloan_fund(3_000_000 * GLMR)
1
		.build()
1
		.execute_with(|| {
1
			// set parachain inherent data
1
			set_parachain_inherent_data();
1
			let init_block = CrowdloanRewards::init_vesting_block();
1
			// This matches the previous vesting
1
			let end_block = init_block + 4 * WEEKS;
1
			// Batch calls always succeed. We just need to check the inner event
1
			assert_ok!(
1
				RuntimeCall::Utility(pallet_utility::Call::<Runtime>::batch_all {
1
					calls: vec![
1
						RuntimeCall::CrowdloanRewards(
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
1
								rewards: vec![(
1
									[4u8; 32].into(),
1
									Some(AccountId::from(CHARLIE)),
1
									1_500_000 * GLMR
1
								)]
1
							}
1
						),
1
						RuntimeCall::CrowdloanRewards(
1
							pallet_crowdloan_rewards::Call::<Runtime>::initialize_reward_vec {
1
								rewards: vec![(
1
									[5u8; 32].into(),
1
									Some(AccountId::from(DAVE)),
1
									1_500_000 * GLMR
1
								)]
1
							}
1
						),
1
						RuntimeCall::CrowdloanRewards(
1
							pallet_crowdloan_rewards::Call::<Runtime>::complete_initialization {
1
								lease_ending_block: end_block
1
							}
1
						)
1
					]
1
				})
1
				.dispatch(root_origin())
1
			);
1
			let crowdloan_precompile_address = H160::from_low_u64_be(2049);
1

            
1
			// Charlie uses the crowdloan precompile to update address through the EVM
1
			let gas_limit = 100000u64;
1
			let gas_price: U256 = BASE_FEE_GENESIS.into();
1

            
1
			// Construct the input data to check if Bob is a contributor
1
			let mut call_data = Vec::<u8>::from([0u8; 36]);
1
			call_data[0..4]
1
				.copy_from_slice(&Keccak256::digest(b"update_reward_address(address)")[0..4]);
1
			call_data[16..36].copy_from_slice(&ALICE);
1

            
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
1
				source: H160::from(CHARLIE),
1
				target: crowdloan_precompile_address,
1
				input: call_data,
1
				value: U256::zero(), // No value sent in EVM
1
				gas_limit,
1
				max_fee_per_gas: gas_price,
1
				max_priority_fee_per_gas: None,
1
				nonce: None, // Use the next nonce
1
				access_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
1
			assert!(CrowdloanRewards::accounts_payable(&AccountId::from(CHARLIE)).is_none());
1
			assert_eq!(
1
				CrowdloanRewards::accounts_payable(&AccountId::from(ALICE))
1
					.unwrap()
1
					.claimed_reward,
1
				(450_000 * GLMR)
1
			);
1
		})
1
}
1
fn run_with_system_weight<F>(w: Weight, mut assertions: F)
1
where
1
	F: FnMut() -> (),
1
{
1
	let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
1
		.build_storage()
1
		.unwrap()
1
		.into();
1
	t.execute_with(|| {
1
		System::set_block_consumed_resources(w, 0);
1
		assertions()
1
	});
1
}
#[test]
#[rustfmt::skip]
1
fn length_fee_is_sensible() {
1
	use sp_runtime::testing::TestXt;
1

            
1
	// tests that length fee is sensible for a few hypothetical transactions
1
	ExtBuilder::default().build().execute_with(|| {
1
		let call = frame_system::Call::remark::<Runtime> { remark: vec![] };
1
		let uxt: TestXt<_, ()> = TestXt::new(call, Some((1u64, ())));
1

            
9
		let calc_fee = |len: u32| -> Balance {
9
			moonbeam_runtime::TransactionPayment::query_fee_details(uxt.clone(), len)
9
				.inclusion_fee
9
				.expect("fee should be calculated")
9
				.len_fee
9
		};
		// editorconfig-checker-disable
		//                  left: cost of length fee, right: size in bytes
		//                             /------------- proportional component: O(N * 1B)
		//                             |           /- exponential component: O(N ** 3)
		//                             |           |
1
		assert_eq!(                    100_000_000_100, calc_fee(1));
1
		assert_eq!(                  1_000_000_100_000, calc_fee(10));
1
		assert_eq!(                 10_000_100_000_000, calc_fee(100));
1
		assert_eq!(                100_100_000_000_000, calc_fee(1_000));
1
		assert_eq!(              1_100_000_000_000_000, calc_fee(10_000)); // inflection point
1
		assert_eq!(            110_000_000_000_000_000, calc_fee(100_000));
1
		assert_eq!(        100_100_000_000_000_000_000, calc_fee(1_000_000)); // 100 GLMR, ~ 1MB
1
		assert_eq!(    100_001_000_000_000_000_000_000, calc_fee(10_000_000));
1
		assert_eq!(100_000_010_000_000_000_000_000_000, calc_fee(100_000_000));
		// editorconfig-checker-enable
1
	});
1
}
#[test]
1
fn multiplier_can_grow_from_zero() {
1
	use frame_support::traits::Get;
1

            
1
	let minimum_multiplier = moonbeam_runtime::MinimumMultiplier::get();
1
	let target = moonbeam_runtime::TargetBlockFullness::get()
1
		* RuntimeBlockWeights::get()
1
			.get(DispatchClass::Normal)
1
			.max_total
1
			.unwrap();
1
	// if the min is too small, then this will not change, and we are doomed forever.
1
	// the weight is 1/100th bigger than target.
1
	run_with_system_weight(target * 101 / 100, || {
1
		let next = moonbeam_runtime::SlowAdjustingFeeUpdate::<Runtime>::convert(minimum_multiplier);
1
		assert!(
1
			next > minimum_multiplier,
			"{:?} !>= {:?}",
			next,
			minimum_multiplier
		);
1
	})
1
}
#[test]
1
fn ethereum_invalid_transaction() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		// Ensure an extrinsic not containing enough gas limit to store the transaction
1
		// on chain is rejected.
1
		assert_eq!(
1
			Executive::apply_extrinsic(unchecked_eth_tx(INVALID_ETH_TX)),
1
			Err(
1
				sp_runtime::transaction_validity::TransactionValidityError::Invalid(
1
					sp_runtime::transaction_validity::InvalidTransaction::Custom(0u8)
1
				)
1
			)
1
		);
1
	});
1
}
#[test]
1
fn initial_gas_fee_is_correct() {
1
	use fp_evm::FeeCalculator;
1

            
1
	ExtBuilder::default().build().execute_with(|| {
1
		let multiplier = TransactionPayment::next_fee_multiplier();
1
		assert_eq!(multiplier, Multiplier::from(1u128));
1
		assert_eq!(
1
			TransactionPaymentAsGasPrice::min_gas_price(),
1
			(
1
				125_000_000_000u128.into(),
1
				Weight::from_parts(41_742_000u64, 0)
1
			)
1
		);
1
	});
1
}
#[test]
1
fn min_gas_fee_is_correct() {
1
	use fp_evm::FeeCalculator;
1
	use frame_support::traits::Hooks;
1

            
1
	ExtBuilder::default().build().execute_with(|| {
1
		pallet_transaction_payment::NextFeeMultiplier::<Runtime>::put(Multiplier::from(0));
1
		TransactionPayment::on_finalize(System::block_number()); // should trigger min to kick in
1

            
1
		let multiplier = TransactionPayment::next_fee_multiplier();
1
		assert_eq!(multiplier, Multiplier::from(1u128));
1
		assert_eq!(
1
			TransactionPaymentAsGasPrice::min_gas_price(),
1
			(
1
				125_000_000_000u128.into(),
1
				Weight::from_parts(41_742_000u64, 0)
1
			)
1
		);
1
	});
1
}
#[test]
1
fn transfer_ed_0_substrate() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), (1 * GLMR) + (1 * WEI)),
1
			(AccountId::from(BOB), 0),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			// Substrate transfer
1
			assert_ok!(Balances::transfer_allow_death(
1
				origin_of(AccountId::from(ALICE)),
1
				AccountId::from(BOB),
1
				1 * GLMR,
1
			));
			// 1 WEI is left in the account
1
			assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI);
1
		});
1
}
#[test]
1
fn transfer_ed_0_evm() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(
1
				AccountId::from(ALICE),
1
				((1 * GLMR) + (21_000 * BASE_FEE_GENESIS)) + (1 * WEI),
1
			),
1
			(AccountId::from(BOB), 0),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			// EVM transfer
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
1
				source: H160::from(ALICE),
1
				target: H160::from(BOB),
1
				input: Vec::new(),
1
				value: (1 * GLMR).into(),
1
				gas_limit: 21_000u64,
1
				max_fee_per_gas: BASE_FEE_GENESIS.into(),
1
				max_priority_fee_per_gas: Some(BASE_FEE_GENESIS.into()),
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
			// 1 WEI is left in the account
1
			assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI,);
1
		});
1
}
#[test]
1
fn refund_ed_0_evm() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(
1
				AccountId::from(ALICE),
1
				((1 * GLMR) + (21_777 * BASE_FEE_GENESIS)),
1
			),
1
			(AccountId::from(BOB), 0),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			// EVM transfer that zeroes ALICE
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
1
				source: H160::from(ALICE),
1
				target: H160::from(BOB),
1
				input: Vec::new(),
1
				value: (1 * GLMR).into(),
1
				gas_limit: 21_777u64,
1
				max_fee_per_gas: BASE_FEE_GENESIS.into(),
1
				max_priority_fee_per_gas: Some(BASE_FEE_GENESIS.into()),
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
			// ALICE is refunded
1
			assert_eq!(
1
				Balances::free_balance(AccountId::from(ALICE)),
1
				777 * BASE_FEE_GENESIS,
1
			);
1
		});
1
}
#[test]
1
fn author_does_not_receive_priority_fee() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(
1
			AccountId::from(BOB),
1
			(1 * GLMR) + (21_000 * (500 * GIGAWEI)),
1
		)])
1
		.build()
1
		.execute_with(|| {
1
			// Some block author as seen by pallet-evm.
1
			let author = AccountId::from(<pallet_evm::Pallet<Runtime>>::find_author());
1
			// Currently the default impl of the evm uses `deposit_into_existing`.
1
			// If we were to use this implementation, and for an author to receive eventual tips,
1
			// the account needs to be somehow initialized, otherwise the deposit would fail.
1
			Balances::make_free_balance_be(&author, 100 * GLMR);
1

            
1
			// EVM transfer.
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
1
				source: H160::from(BOB),
1
				target: H160::from(ALICE),
1
				input: Vec::new(),
1
				value: (1 * GLMR).into(),
1
				gas_limit: 21_000u64,
1
				max_fee_per_gas: U256::from(300 * GIGAWEI),
1
				max_priority_fee_per_gas: Some(U256::from(200 * GIGAWEI)),
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
			// Author free balance didn't change.
1
			assert_eq!(Balances::free_balance(author), 100 * GLMR,);
1
		});
1
}
#[test]
1
fn total_issuance_after_evm_transaction_with_priority_fee() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(
1
			AccountId::from(BOB),
1
			(1 * GLMR) + (21_000 * (200 * GIGAWEI)),
1
		)])
1
		.build()
1
		.execute_with(|| {
1
			let issuance_before = <Runtime as pallet_evm::Config>::Currency::total_issuance();
1
			// EVM transfer.
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
1
				source: H160::from(BOB),
1
				target: H160::from(ALICE),
1
				input: Vec::new(),
1
				value: (1 * GLMR).into(),
1
				gas_limit: 21_000u64,
1
				max_fee_per_gas: U256::from(200 * GIGAWEI),
1
				max_priority_fee_per_gas: Some(U256::from(100 * GIGAWEI)),
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
1
			let issuance_after = <Runtime as pallet_evm::Config>::Currency::total_issuance();
1
			// Fee is 100 GWEI base fee + 100 GWEI tip.
1
			let fee = ((200 * GIGAWEI) * 21_000) as f64;
1
			// 80% was burned.
1
			let expected_burn = (fee * 0.8) as u128;
1
			assert_eq!(issuance_after, issuance_before - expected_burn,);
			// 20% was sent to treasury.
1
			let expected_treasury = (fee * 0.2) as u128;
1
			assert_eq!(moonbeam_runtime::Treasury::pot(), expected_treasury);
1
		});
1
}
#[test]
1
fn total_issuance_after_evm_transaction_without_priority_fee() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(
1
			AccountId::from(BOB),
1
			(1 * GLMR) + (21_000 * BASE_FEE_GENESIS),
1
		)])
1
		.build()
1
		.execute_with(|| {
1
			let issuance_before = <Runtime as pallet_evm::Config>::Currency::total_issuance();
1
			// EVM transfer.
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
1
				source: H160::from(BOB),
1
				target: H160::from(ALICE),
1
				input: Vec::new(),
1
				value: (1 * GLMR).into(),
1
				gas_limit: 21_000u64,
1
				max_fee_per_gas: BASE_FEE_GENESIS.into(),
1
				max_priority_fee_per_gas: Some(BASE_FEE_GENESIS.into()),
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
1
			let issuance_after = <Runtime as pallet_evm::Config>::Currency::total_issuance();
1
			// Fee is 100 GWEI base fee.
1
			let fee = (BASE_FEE_GENESIS * 21_000) as f64;
1
			// 80% was burned.
1
			let expected_burn = (fee * 0.8) as u128;
1
			assert_eq!(issuance_after, issuance_before - expected_burn,);
			// 20% was sent to treasury.
1
			let expected_treasury = (fee * 0.2) as u128;
1
			assert_eq!(moonbeam_runtime::Treasury::pot(), expected_treasury);
1
		});
1
}
#[test]
1
fn root_can_change_default_xcm_vers() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
1
			metadata: AssetRegistrarMetadata {
1
				name: b"RelayToken".to_vec(),
1
				symbol: b"Relay".to_vec(),
1
				decimals: 12,
1
				is_frozen: false,
1
			},
1
			balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)],
1
			is_sufficient: true,
1
		}])
1
		.build()
1
		.execute_with(|| {
1
			let source_location = AssetType::Xcm(xcm::v3::Location::parent());
1
			let source_id: moonbeam_runtime::AssetId = source_location.clone().into();
1
			let asset = currency_to_asset(CurrencyId::ForeignAsset(source_id), 100_000_000_000_000);
1
			// Default XCM version is not set yet, so xtokens should fail because it does not
1
			// know with which version to send
1
			assert_noop!(
1
				PolkadotXcm::transfer_assets(
1
					origin_of(AccountId::from(ALICE)),
1
					Box::new(VersionedLocation::V4(Location::parent())),
1
					Box::new(VersionedLocation::V4(Location {
1
						parents: 0,
1
						interior: [AccountId32 {
1
							network: None,
1
							id: [1u8; 32],
1
						}]
1
						.into(),
1
					})),
1
					Box::new(VersionedAssets::V4(asset.clone().into())),
1
					0,
1
					WeightLimit::Unlimited
1
				),
1
				pallet_xcm::Error::<Runtime>::SendFailure
1
			);
			// Root sets the defaultXcm
1
			assert_ok!(PolkadotXcm::force_default_xcm_version(
1
				root_origin(),
1
				Some(2)
1
			));
			// Now transferring does not fail
1
			assert_ok!(PolkadotXcm::transfer_assets(
1
				origin_of(AccountId::from(ALICE)),
1
				Box::new(VersionedLocation::V4(Location::parent())),
1
				Box::new(VersionedLocation::V4(Location {
1
					parents: 0,
1
					interior: [AccountId32 {
1
						network: None,
1
						id: [1u8; 32],
1
					}]
1
					.into(),
1
				})),
1
				Box::new(VersionedAssets::V4(asset.clone().into())),
1
				0,
1
				WeightLimit::Unlimited
1
			));
1
		})
1
}
#[test]
1
fn asset_can_be_registered() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		let source_location = AssetType::Xcm(xcm::v3::Location::parent());
1
		let source_id: moonbeam_runtime::AssetId = source_location.clone().into();
1
		let asset_metadata = AssetRegistrarMetadata {
1
			name: b"RelayToken".to_vec(),
1
			symbol: b"Relay".to_vec(),
1
			decimals: 12,
1
			is_frozen: false,
1
		};
1
		assert_ok!(AssetManager::register_foreign_asset(
1
			moonbeam_runtime::RuntimeOrigin::root(),
1
			source_location,
1
			asset_metadata,
1
			1u128,
1
			true
1
		));
1
		assert!(AssetManager::asset_id_type(source_id).is_some());
1
	});
1
}
#[test]
1
fn xcm_asset_erc20_precompiles_supply_and_balance() {
1
	ExtBuilder::default()
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
1
			metadata: AssetRegistrarMetadata {
1
				name: b"RelayToken".to_vec(),
1
				symbol: b"Relay".to_vec(),
1
				decimals: 12,
1
				is_frozen: false,
1
			},
1
			balances: vec![(AccountId::from(ALICE), 1_000 * GLMR)],
1
			is_sufficient: true,
1
		}])
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			// We have the assetId that corresponds to the relay chain registered
1
			let relay_asset_id: moonbeam_runtime::AssetId =
1
				AssetType::Xcm(xcm::v3::Location::parent()).into();
1

            
1
			// Its address is
1
			let asset_precompile_address = Runtime::asset_id_to_account(
1
				FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
1
				relay_asset_id,
1
			);
1

            
1
			// Assert the asset has been created with the correct supply
1
			assert_eq!(
1
				moonbeam_runtime::Assets::total_supply(relay_asset_id),
1
				1_000 * GLMR
1
			);
			// Access totalSupply through precompile. Important that the context is correct
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					asset_precompile_address,
1
					ForeignAssetsPCall::total_supply {},
1
				)
1
				.expect_cost(3338)
1
				.expect_no_logs()
1
				.execute_returns(U256::from(1000 * GLMR));
1

            
1
			// Access balanceOf through precompile
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					asset_precompile_address,
1
					ForeignAssetsPCall::balance_of {
1
						who: Address(ALICE.into()),
1
					},
1
				)
1
				.expect_cost(3338)
1
				.expect_no_logs()
1
				.execute_returns(U256::from(1000 * GLMR));
1
		});
1
}
#[test]
1
fn xcm_asset_erc20_precompiles_transfer() {
1
	ExtBuilder::default()
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
1
			metadata: AssetRegistrarMetadata {
1
				name: b"RelayToken".to_vec(),
1
				symbol: b"Relay".to_vec(),
1
				decimals: 12,
1
				is_frozen: false,
1
			},
1
			balances: vec![(AccountId::from(ALICE), 1_000 * GLMR)],
1
			is_sufficient: true,
1
		}])
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			// We have the assetId that corresponds to the relay chain registered
1
			let relay_asset_id: moonbeam_runtime::AssetId =
1
				AssetType::Xcm(xcm::v3::Location::parent()).into();
1

            
1
			// Its address is
1
			let asset_precompile_address = Runtime::asset_id_to_account(
1
				FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
1
				relay_asset_id,
1
			);
1

            
1
			// Transfer tokens from Aice to Bob, 400 GLMR.
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					asset_precompile_address,
1
					ForeignAssetsPCall::transfer {
1
						to: Address(BOB.into()),
1
						value: { 400 * GLMR }.into(),
1
					},
1
				)
1
				.expect_cost(24684)
1
				.expect_log(log3(
1
					asset_precompile_address,
1
					SELECTOR_LOG_TRANSFER,
1
					H160::from(ALICE),
1
					H160::from(BOB),
1
					solidity::encode_event_data(U256::from(400 * GLMR)),
1
				))
1
				.execute_returns(true);
1

            
1
			// Make sure BOB has 400 GLMR
1
			Precompiles::new()
1
				.prepare_test(
1
					BOB,
1
					asset_precompile_address,
1
					ForeignAssetsPCall::balance_of {
1
						who: Address(BOB.into()),
1
					},
1
				)
1
				.expect_cost(3338)
1
				.expect_no_logs()
1
				.execute_returns(U256::from(400 * GLMR));
1
		});
1
}
#[test]
1
fn xcm_asset_erc20_precompiles_approve() {
1
	ExtBuilder::default()
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
1
			metadata: AssetRegistrarMetadata {
1
				name: b"RelayToken".to_vec(),
1
				symbol: b"Relay".to_vec(),
1
				decimals: 12,
1
				is_frozen: false,
1
			},
1
			balances: vec![(AccountId::from(ALICE), 1_000 * GLMR)],
1
			is_sufficient: true,
1
		}])
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			// We have the assetId that corresponds to the relay chain registered
1
			let relay_asset_id: moonbeam_runtime::AssetId =
1
				AssetType::Xcm(xcm::v3::Location::parent()).into();
1

            
1
			// Its address is
1
			let asset_precompile_address = Runtime::asset_id_to_account(
1
				FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
1
				relay_asset_id,
1
			);
1

            
1
			// Aprove Bob for spending 400 GLMR from Alice
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					asset_precompile_address,
1
					ForeignAssetsPCall::approve {
1
						spender: Address(BOB.into()),
1
						value: { 400 * GLMR }.into(),
1
					},
1
				)
1
				.expect_cost(15573)
1
				.expect_log(log3(
1
					asset_precompile_address,
1
					SELECTOR_LOG_APPROVAL,
1
					H160::from(ALICE),
1
					H160::from(BOB),
1
					solidity::encode_event_data(U256::from(400 * GLMR)),
1
				))
1
				.execute_returns(true);
1

            
1
			// Transfer tokens from Alice to Charlie by using BOB as origin
1
			Precompiles::new()
1
				.prepare_test(
1
					BOB,
1
					asset_precompile_address,
1
					ForeignAssetsPCall::transfer_from {
1
						from: Address(ALICE.into()),
1
						to: Address(CHARLIE.into()),
1
						value: { 400 * GLMR }.into(),
1
					},
1
				)
1
				.expect_cost(29947)
1
				.expect_log(log3(
1
					asset_precompile_address,
1
					SELECTOR_LOG_TRANSFER,
1
					H160::from(ALICE),
1
					H160::from(CHARLIE),
1
					solidity::encode_event_data(U256::from(400 * GLMR)),
1
				))
1
				.execute_returns(true);
1

            
1
			// Make sure CHARLIE has 400 GLMR
1
			Precompiles::new()
1
				.prepare_test(
1
					CHARLIE,
1
					asset_precompile_address,
1
					ForeignAssetsPCall::balance_of {
1
						who: Address(CHARLIE.into()),
1
					},
1
				)
1
				.expect_cost(3338)
1
				.expect_no_logs()
1
				.execute_returns(U256::from(400 * GLMR));
1
		});
1
}
#[test]
1
fn xtokens_precompile_transfer() {
1
	ExtBuilder::default()
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
1
			metadata: AssetRegistrarMetadata {
1
				name: b"RelayToken".to_vec(),
1
				symbol: b"Relay".to_vec(),
1
				decimals: 12,
1
				is_frozen: false,
1
			},
1
			balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)],
1
			is_sufficient: true,
1
		}])
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_safe_xcm_version(2)
1
		.build()
1
		.execute_with(|| {
1
			let xtokens_precompile_address = H160::from_low_u64_be(2052);
1

            
1
			// We have the assetId that corresponds to the relay chain registered
1
			let relay_asset_id: moonbeam_runtime::AssetId =
1
				AssetType::Xcm(xcm::v3::Location::parent()).into();
1

            
1
			// Its address is
1
			let asset_precompile_address = Runtime::asset_id_to_account(
1
				FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
1
				relay_asset_id,
1
			);
1

            
1
			// Alice has 1000 tokens. She should be able to send through precompile
1
			let destination = Location::new(
1
				1,
1
				[Junction::AccountId32 {
1
					network: None,
1
					id: [1u8; 32],
1
				}],
1
			);
1

            
1
			// We use the address of the asset as an identifier of the asset we want to transfer
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					xtokens_precompile_address,
1
					XtokensPCall::transfer {
1
						currency_address: Address(asset_precompile_address.into()),
1
						amount: 500_000_000_000_000u128.into(),
1
						destination,
1
						weight: 4_000_000,
1
					},
1
				)
1
				.expect_cost(24691)
1
				.expect_no_logs()
1
				.execute_returns(())
1
		})
1
}
#[test]
1
fn xtokens_precompile_transfer_multiasset() {
1
	ExtBuilder::default()
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
1
			metadata: AssetRegistrarMetadata {
1
				name: b"RelayToken".to_vec(),
1
				symbol: b"Relay".to_vec(),
1
				decimals: 12,
1
				is_frozen: false,
1
			},
1
			balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)],
1
			is_sufficient: true,
1
		}])
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_safe_xcm_version(2)
1
		.build()
1
		.execute_with(|| {
1
			let xtokens_precompile_address = H160::from_low_u64_be(2052);
1

            
1
			// Alice has 1000 tokens. She should be able to send through precompile
1
			let destination = Location::new(
1
				1,
1
				[Junction::AccountId32 {
1
					network: None,
1
					id: [1u8; 32],
1
				}],
1
			);
1

            
1
			// This time we transfer it through TransferMultiAsset
1
			// Instead of the address, we encode directly the multilocation referencing the asset
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					xtokens_precompile_address,
1
					XtokensPCall::transfer_multiasset {
1
						// We want to transfer the relay token
1
						asset: Location::parent(),
1
						amount: 500_000_000_000_000u128.into(),
1
						destination,
1
						weight: 4_000_000,
1
					},
1
				)
1
				.expect_cost(24691)
1
				.expect_no_logs()
1
				.execute_returns(());
1
		})
1
}
#[test]
1
fn make_sure_glmr_can_be_transferred_precompile() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)])
1
		.with_mappings(vec![(
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
1
			AccountId::from(ALICE),
1
		)])
1
		.with_safe_xcm_version(2)
1
		.build()
1
		.execute_with(|| {
1
			assert_ok!(PolkadotXcm::transfer_assets(
1
				origin_of(AccountId::from(ALICE)),
1
				Box::new(VersionedLocation::V4(Location::parent())),
1
				Box::new(VersionedLocation::V4(Location {
1
					parents: 0,
1
					interior: [AccountId32 {
1
						network: None,
1
						id: [1u8; 32],
1
					}]
1
					.into(),
1
				})),
1
				Box::new(VersionedAssets::V4(
1
					Asset {
1
						id: AssetId(moonbeam_runtime::xcm_config::SelfReserve::get()),
1
						fun: Fungible(1000)
1
					}
1
					.into()
1
				)),
1
				0,
1
				WeightLimit::Limited(40000.into())
1
			));
1
		});
1
}
#[test]
1
fn make_sure_glmr_can_be_transferred() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)])
1
		.with_mappings(vec![(
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
1
			AccountId::from(ALICE),
1
		)])
1
		.with_safe_xcm_version(2)
1
		.build()
1
		.execute_with(|| {
1
			let dest = Location {
1
				parents: 1,
1
				interior: [AccountId32 {
1
					network: None,
1
					id: [1u8; 32],
1
				}]
1
				.into(),
1
			};
1
			assert_ok!(PolkadotXcm::transfer_assets(
1
				origin_of(AccountId::from(ALICE)),
1
				Box::new(VersionedLocation::V4(Location::parent())),
1
				Box::new(VersionedLocation::V4(dest)),
1
				Box::new(VersionedAssets::V4(
1
					Asset {
1
						id: AssetId(moonbeam_runtime::xcm_config::SelfReserve::get()),
1
						fun: Fungible(100)
1
					}
1
					.into()
1
				)),
1
				0,
1
				WeightLimit::Limited(40000.into())
1
			));
1
		});
1
}
#[test]
1
fn make_sure_polkadot_xcm_cannot_be_called() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_collators(vec![(AccountId::from(ALICE), 1_000 * GLMR)])
1
		.with_mappings(vec![(
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
1
			AccountId::from(ALICE),
1
		)])
1
		.build()
1
		.execute_with(|| {
1
			let dest = Location {
1
				parents: 1,
1
				interior: [AccountId32 {
1
					network: None,
1
					id: [1u8; 32],
1
				}]
1
				.into(),
1
			};
1
			let assets: Assets = [Asset {
1
				id: AssetId(moonbeam_runtime::xcm_config::SelfLocation::get()),
1
				fun: Fungible(1000),
1
			}]
1
			.to_vec()
1
			.into();
1
			assert_noop!(
1
				RuntimeCall::PolkadotXcm(pallet_xcm::Call::<Runtime>::reserve_transfer_assets {
1
					dest: Box::new(VersionedLocation::V4(dest.clone())),
1
					beneficiary: Box::new(VersionedLocation::V4(dest)),
1
					assets: Box::new(VersionedAssets::V4(assets)),
1
					fee_asset_item: 0,
1
				})
1
				.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::signed(
1
					AccountId::from(ALICE)
1
				)),
1
				frame_system::Error::<Runtime>::CallFiltered
1
			);
1
		});
1
}
#[test]
1
fn transact_through_signed_precompile_works_v2() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_safe_xcm_version(2)
1
		.build()
1
		.execute_with(|| {
1
			// Destination
1
			let dest = Location::parent();
1

            
1
			let fee_payer_asset = Location::parent();
1

            
1
			let bytes = vec![1u8, 2u8, 3u8];
1

            
1
			let total_weight = 1_000_000_000u64;
1

            
1
			let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061);
1

            
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					xcm_transactor_v2_precompile_address,
1
					XcmTransactorV2PCall::transact_through_signed_multilocation {
1
						dest,
1
						fee_asset: fee_payer_asset,
1
						weight: 4_000_000,
1
						call: bytes.into(),
1
						fee_amount: u128::from(total_weight).into(),
1
						overall_weight: total_weight,
1
					},
1
				)
1
				.expect_cost(23275)
1
				.expect_no_logs()
1
				.execute_returns(());
1
		});
1
}
#[test]
1
fn transact_through_signed_cannot_send_to_local_chain() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_safe_xcm_version(2)
1
		.build()
1
		.execute_with(|| {
1
			// Destination
1
			let dest = Location::here();
1

            
1
			let fee_payer_asset = Location::parent();
1

            
1
			let bytes = vec![1u8, 2u8, 3u8];
1

            
1
			let total_weight = 1_000_000_000u64;
1

            
1
			let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061);
1

            
1
			Precompiles::new()
1
				.prepare_test(
1
					ALICE,
1
					xcm_transactor_v2_precompile_address,
1
					XcmTransactorV2PCall::transact_through_signed_multilocation {
1
						dest,
1
						fee_asset: fee_payer_asset,
1
						weight: 4_000_000,
1
						call: bytes.into(),
1
						fee_amount: u128::from(total_weight).into(),
1
						overall_weight: total_weight,
1
					},
1
				)
1
				.execute_reverts(|output| {
1
					from_utf8(&output)
1
						.unwrap()
1
						.contains("Dispatched call failed with error:")
1
						&& from_utf8(&output).unwrap().contains("ErrorValidating")
1
				});
1
		});
1
}
#[test]
1
fn transactor_cannot_use_more_than_max_weight() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
1
			metadata: AssetRegistrarMetadata {
1
				name: b"RelayToken".to_vec(),
1
				symbol: b"Relay".to_vec(),
1
				decimals: 12,
1
				is_frozen: false,
1
			},
1
			balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)],
1
			is_sufficient: true,
1
		}])
1
		.build()
1
		.execute_with(|| {
1
			let source_location = AssetType::Xcm(xcm::v3::Location::parent());
1
			let source_id: moonbeam_runtime::AssetId = source_location.clone().into();
1
			assert_ok!(XcmTransactor::register(
1
				root_origin(),
1
				AccountId::from(ALICE),
1
				0,
1
			));
			// Root can set transact info
1
			assert_ok!(XcmTransactor::set_transact_info(
1
				root_origin(),
1
				Box::new(xcm::VersionedLocation::V4(Location::parent())),
1
				// Relay charges 1000 for every instruction, and we have 3, so 3000
1
				3000.into(),
1
				20000.into(),
1
				None
1
			));
			// Root can set transact info
1
			assert_ok!(XcmTransactor::set_fee_per_second(
1
				root_origin(),
1
				Box::new(xcm::VersionedLocation::V4(Location::parent())),
1
				1,
1
			));
1
			assert_noop!(
1
				XcmTransactor::transact_through_derivative(
1
					origin_of(AccountId::from(ALICE)),
1
					moonbeam_runtime::xcm_config::Transactors::Relay,
1
					0,
1
					CurrencyPayment {
1
						currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::V4(
1
							Location::parent()
1
						))),
1
						fee_amount: None
1
					},
1
					vec![],
1
					// 2000 is the max
1
					TransactWeights {
1
						transact_required_weight_at_most: 17001.into(),
1
						overall_weight: None
1
					},
1
					false
1
				),
1
				pallet_xcm_transactor::Error::<Runtime>::MaxWeightTransactReached
1
			);
1
			assert_noop!(
1
				XcmTransactor::transact_through_derivative(
1
					origin_of(AccountId::from(ALICE)),
1
					moonbeam_runtime::xcm_config::Transactors::Relay,
1
					0,
1
					CurrencyPayment {
1
						currency: Currency::AsCurrencyId(CurrencyId::ForeignAsset(source_id)),
1
						fee_amount: None
1
					},
1
					vec![],
1
					// 20000 is the max
1
					TransactWeights {
1
						transact_required_weight_at_most: 17001.into(),
1
						overall_weight: None
1
					},
1
					false
1
				),
1
				pallet_xcm_transactor::Error::<Runtime>::MaxWeightTransactReached
1
			);
1
		})
1
}
#[test]
1
fn call_xtokens_with_fee() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * GLMR),
1
			(AccountId::from(BOB), 1_000 * GLMR),
1
		])
1
		.with_safe_xcm_version(2)
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
1
			metadata: AssetRegistrarMetadata {
1
				name: b"RelayToken".to_vec(),
1
				symbol: b"Relay".to_vec(),
1
				decimals: 12,
1
				is_frozen: false,
1
			},
1
			balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)],
1
			is_sufficient: true,
1
		}])
1
		.build()
1
		.execute_with(|| {
1
			let source_location = AssetType::Xcm(xcm::v3::Location::parent());
1
			let dest = Location {
1
				parents: 1,
1
				interior: [AccountId32 {
1
					network: None,
1
					id: [1u8; 32],
1
				}]
1
				.into(),
1
			};
1
			let source_id: moonbeam_runtime::AssetId = source_location.clone().into();
1

            
1
			let before_balance =
1
				moonbeam_runtime::Assets::balance(source_id, &AccountId::from(ALICE));
1
			let (chain_part, beneficiary) =
1
				split_location_into_chain_part_and_beneficiary(dest).unwrap();
1
			let asset = currency_to_asset(CurrencyId::ForeignAsset(source_id), 100_000_000_000_000);
1
			let asset_fee = currency_to_asset(CurrencyId::ForeignAsset(source_id), 100);
1
			// We are able to transfer with fee
1
			assert_ok!(PolkadotXcm::transfer_assets(
1
				origin_of(AccountId::from(ALICE)),
1
				Box::new(VersionedLocation::V4(chain_part)),
1
				Box::new(VersionedLocation::V4(beneficiary)),
1
				Box::new(VersionedAssets::V4(vec![asset_fee, asset].into())),
1
				0,
1
				WeightLimit::Limited(4000000000.into())
1
			));
1
			let after_balance =
1
				moonbeam_runtime::Assets::balance(source_id, &AccountId::from(ALICE));
1
			// At least these much (plus fees) should have been charged
1
			assert_eq!(before_balance - 100_000_000_000_000 - 100, after_balance);
1
		});
1
}
#[test]
1
fn test_xcm_utils_ml_tp_account() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		let xcm_utils_precompile_address = H160::from_low_u64_be(2060);
1
		let expected_address_parent: H160 =
1
			ParentIsPreset::<AccountId>::convert_location(&Location::parent())
1
				.unwrap()
1
				.into();
1

            
1
		Precompiles::new()
1
			.prepare_test(
1
				ALICE,
1
				xcm_utils_precompile_address,
1
				XcmUtilsPCall::multilocation_to_address {
1
					location: Location::parent(),
1
				},
1
			)
1
			.expect_cost(1669)
1
			.expect_no_logs()
1
			.execute_returns(Address(expected_address_parent));
1

            
1
		let parachain_2000_location = Location::new(1, [Parachain(2000)]);
1
		let expected_address_parachain: H160 =
1
			SiblingParachainConvertsVia::<Sibling, AccountId>::convert_location(
1
				&parachain_2000_location,
1
			)
1
			.unwrap()
1
			.into();
1

            
1
		Precompiles::new()
1
			.prepare_test(
1
				ALICE,
1
				xcm_utils_precompile_address,
1
				XcmUtilsPCall::multilocation_to_address {
1
					location: parachain_2000_location,
1
				},
1
			)
1
			.expect_cost(1669)
1
			.expect_no_logs()
1
			.execute_returns(Address(expected_address_parachain));
1

            
1
		let alice_in_parachain_2000_location = Location::new(
1
			1,
1
			[
1
				Parachain(2000),
1
				AccountKey20 {
1
					network: None,
1
					key: ALICE,
1
				},
1
			],
1
		);
1
		let expected_address_alice_in_parachain_2000 =
1
			xcm_builder::HashedDescription::<
1
				AccountId,
1
				xcm_builder::DescribeFamily<xcm_builder::DescribeAllTerminal>,
1
			>::convert_location(&alice_in_parachain_2000_location)
1
			.unwrap()
1
			.into();
1

            
1
		Precompiles::new()
1
			.prepare_test(
1
				ALICE,
1
				xcm_utils_precompile_address,
1
				XcmUtilsPCall::multilocation_to_address {
1
					location: alice_in_parachain_2000_location,
1
				},
1
			)
1
			.expect_cost(1669)
1
			.expect_no_logs()
1
			.execute_returns(Address(expected_address_alice_in_parachain_2000));
1
	});
1
}
#[test]
1
fn test_xcm_utils_weight_message() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		let xcm_utils_precompile_address = H160::from_low_u64_be(2060);
1
		let expected_weight =
1
			XcmWeight::<moonbeam_runtime::Runtime, RuntimeCall>::clear_origin().ref_time();
1

            
1
		let message: Vec<u8> = xcm::VersionedXcm::<()>::V4(Xcm(vec![ClearOrigin])).encode();
1

            
1
		let input = XcmUtilsPCall::weight_message {
1
			message: message.into(),
1
		};
1

            
1
		Precompiles::new()
1
			.prepare_test(ALICE, xcm_utils_precompile_address, input)
1
			.expect_cost(0)
1
			.expect_no_logs()
1
			.execute_returns(expected_weight);
1
	});
1
}
#[test]
1
fn test_xcm_utils_get_units_per_second() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		let xcm_utils_precompile_address = H160::from_low_u64_be(2060);
1
		let location = SelfReserve::get();
1

            
1
		let input = XcmUtilsPCall::get_units_per_second { location };
1

            
1
		let expected_units =
1
			WEIGHT_REF_TIME_PER_SECOND as u128 * moonbeam_runtime::currency::WEIGHT_FEE;
1

            
1
		Precompiles::new()
1
			.prepare_test(ALICE, xcm_utils_precompile_address, input)
1
			.expect_cost(1669)
1
			.expect_no_logs()
1
			.execute_returns(expected_units);
1
	});
1
}
#[test]
1
fn precompile_existence() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		let precompiles = Precompiles::new();
1
		let precompile_addresses: std::collections::BTreeSet<_> = vec![
1
			1, 2, 3, 4, 5, 6, 7, 8, 9, 256, 1024, 1025, 1026, 2048, 2049, 2050, 2051, 2052, 2053,
1
			2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067,
1
			2068, 2069, 2070, 2071, 2072, 2073, 2074,
1
		]
1
		.into_iter()
1
		.map(H160::from_low_u64_be)
1
		.collect();
3001
		for i in 0..3000 {
3000
			let address = H160::from_low_u64_be(i);
3000

            
3000
			if precompile_addresses.contains(&address) {
40
				assert!(
40
					is_precompile_or_fail::<Runtime>(address, 100_000u64).expect("to be ok"),
					"is_precompile({}) should return true",
					i
				);
40
				assert!(
40
					precompiles
40
						.execute(&mut MockHandle::new(
40
							address,
40
							Context {
40
								address,
40
								caller: H160::zero(),
40
								apparent_value: U256::zero()
40
							}
40
						),)
40
						.is_some(),
					"execute({},..) should return Some(_)",
					i
				);
			} else {
2960
				assert!(
2960
					!is_precompile_or_fail::<Runtime>(address, 100_000u64).expect("to be ok"),
					"is_precompile({}) should return false",
					i
				);
2960
				assert!(
2960
					precompiles
2960
						.execute(&mut MockHandle::new(
2960
							address,
2960
							Context {
2960
								address,
2960
								caller: H160::zero(),
2960
								apparent_value: U256::zero()
2960
							}
2960
						),)
2960
						.is_none(),
					"execute({},..) should return None",
					i
				);
			}
		}
1
	});
1
}
#[test]
1
fn removed_precompiles() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		let precompiles = Precompiles::new();
1
		let removed_precompiles = [1025, 2051, 2062, 2063];
3000
		for i in 1..3000 {
2999
			let address = H160::from_low_u64_be(i);
2999

            
2999
			if !is_precompile_or_fail::<Runtime>(address, 100_000u64).expect("to be ok") {
2959
				continue;
40
			}
40

            
40
			if !removed_precompiles.contains(&i) {
36
				assert!(
36
					match precompiles.is_active_precompile(address, 100_000u64) {
36
						IsPrecompileResult::Answer { is_precompile, .. } => is_precompile,
						_ => false,
					},
					"{i} should be an active precompile"
				);
36
				continue;
4
			}
4

            
4
			assert!(
4
				!match precompiles.is_active_precompile(address, 100_000u64) {
4
					IsPrecompileResult::Answer { is_precompile, .. } => is_precompile,
					_ => false,
				},
				"{i} shouldn't be an active precompile"
			);
4
			precompiles
4
				.prepare_test(Alice, address, [])
4
				.execute_reverts(|out| out == b"Removed precompile");
		}
1
	})
1
}
#[test]
1
fn deal_with_fees_handles_tip() {
1
	use frame_support::traits::OnUnbalanced;
1
	use moonbeam_runtime::{DealWithFees, Treasury};
1

            
1
	ExtBuilder::default().build().execute_with(|| {
1
		// This test checks the functionality of the `DealWithFees` trait implementation in the runtime.
1
		// It simulates a scenario where a fee and a tip are issued to an account and ensures that the
1
		// treasury receives the correct amount (20% of the total), and the rest is burned (80%).
1
		//
1
		// The test follows these steps:
1
		// 1. It issues a fee of 100 and a tip of 1000.
1
		// 2. It checks the total supply before the fee and tip are dealt with, which should be 1_100.
1
		// 3. It checks that the treasury's balance is initially 0.
1
		// 4. It calls `DealWithFees::on_unbalanceds` with the fee and tip.
1
		// 5. It checks that the treasury's balance is now 220 (20% of the fee and tip).
1
		// 6. It checks that the total supply has decreased by 880 (80% of the fee and tip), indicating
1
		//    that this amount was burned.
1
		let fee = <pallet_balances::Pallet<Runtime> as frame_support::traits::fungible::Balanced<
1
			AccountId,
1
		>>::issue(100);
1
		let tip = <pallet_balances::Pallet<Runtime> as frame_support::traits::fungible::Balanced<
1
			AccountId,
1
		>>::issue(1000);
1

            
1
		let total_supply_before = Balances::total_issuance();
1
		assert_eq!(total_supply_before, 1_100);
1
		assert_eq!(Balances::free_balance(&Treasury::account_id()), 0);
1
		DealWithFees::on_unbalanceds(vec![fee, tip].into_iter());
1

            
1
		// treasury should have received 20%
1
		assert_eq!(Balances::free_balance(&Treasury::account_id()), 220);
		// verify 80% burned
1
		let total_supply_after = Balances::total_issuance();
1
		assert_eq!(total_supply_before - total_supply_after, 880);
1
	});
1
}
#[test]
1
fn evm_revert_substrate_events() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(AccountId::from(ALICE), 100_000 * GLMR)])
1
		.build()
1
		.execute_with(|| {
1
			let batch_precompile_address = H160::from_low_u64_be(2056);
1

            
1
			// Batch a transfer followed by an invalid call to batch.
1
			// Thus BatchAll will revert the transfer.
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::call {
1
				source: ALICE.into(),
1
				target: batch_precompile_address,
1

            
1
				input: BatchPCall::batch_all {
1
					to: vec![Address(BOB.into()), Address(batch_precompile_address)].into(),
1
					value: vec![U256::from(1 * GLMR), U256::zero()].into(),
1
					call_data: vec![].into(),
1
					gas_limit: vec![].into()
1
				}
1
				.into(),
1
				value: U256::zero(), // No value sent in EVM
1
				gas_limit: 500_000,
1
				max_fee_per_gas: BASE_FEE_GENESIS.into(),
1
				max_priority_fee_per_gas: None,
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
1
			let transfer_count = System::events()
1
				.iter()
6
				.filter(|r| match r.event {
					RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true,
6
					_ => false,
6
				})
1
				.count();
1

            
1
			assert_eq!(transfer_count, 0, "there should be no transfer event");
1
		});
1
}
#[test]
1
fn evm_success_keeps_substrate_events() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(AccountId::from(ALICE), 100_000 * GLMR)])
1
		.build()
1
		.execute_with(|| {
1
			let batch_precompile_address = H160::from_low_u64_be(2056);
1

            
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::call {
1
				source: ALICE.into(),
1
				target: batch_precompile_address,
1
				input: BatchPCall::batch_all {
1
					to: vec![Address(BOB.into())].into(),
1
					value: vec![U256::from(1 * GLMR)].into(),
1
					call_data: vec![].into(),
1
					gas_limit: vec![].into()
1
				}
1
				.into(),
1
				value: U256::zero(), // No value sent in EVM
1
				gas_limit: 500_000,
1
				max_fee_per_gas: BASE_FEE_GENESIS.into(),
1
				max_priority_fee_per_gas: None,
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
1
			let transfer_count = System::events()
1
				.iter()
10
				.filter(|r| match r.event {
1
					RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true,
9
					_ => false,
10
				})
1
				.count();
1

            
1
			assert_eq!(transfer_count, 1, "there should be 1 transfer event");
1
		});
1
}
#[cfg(test)]
mod fee_tests {
	use super::*;
	use fp_evm::FeeCalculator;
	use frame_support::{
		traits::{ConstU128, OnFinalize},
		weights::{ConstantMultiplier, WeightToFee},
	};
	use moonbeam_runtime::{
		currency, LengthToFee, MinimumMultiplier, RuntimeBlockWeights, SlowAdjustingFeeUpdate,
		TargetBlockFullness, TransactionPaymentAsGasPrice, NORMAL_WEIGHT, WEIGHT_PER_GAS,
	};
	use sp_core::Get;
	use sp_runtime::{BuildStorage, FixedPointNumber, Perbill};
1
	fn run_with_system_weight<F>(w: Weight, mut assertions: F)
1
	where
1
		F: FnMut() -> (),
1
	{
1
		let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
1
			.build_storage()
1
			.unwrap()
1
			.into();
1
		t.execute_with(|| {
1
			System::set_block_consumed_resources(w, 0);
1
			assertions()
1
		});
1
	}
	#[test]
1
	fn test_multiplier_can_grow_from_zero() {
1
		let minimum_multiplier = MinimumMultiplier::get();
1
		let target = TargetBlockFullness::get()
1
			* RuntimeBlockWeights::get()
1
				.get(DispatchClass::Normal)
1
				.max_total
1
				.unwrap();
1
		// if the min is too small, then this will not change, and we are doomed forever.
1
		// the weight is 1/100th bigger than target.
1
		run_with_system_weight(target * 101 / 100, || {
1
			let next = SlowAdjustingFeeUpdate::<Runtime>::convert(minimum_multiplier);
1
			assert!(
1
				next > minimum_multiplier,
				"{:?} !>= {:?}",
				next,
				minimum_multiplier
			);
1
		})
1
	}
	#[test]
1
	fn test_fee_calculation() {
1
		let base_extrinsic = RuntimeBlockWeights::get()
1
			.get(DispatchClass::Normal)
1
			.base_extrinsic;
1
		let multiplier = sp_runtime::FixedU128::from_float(0.999000000000000000);
1
		let extrinsic_len = 100u32;
1
		let extrinsic_weight = 5_000u64;
1
		let tip = 42u128;
1
		type WeightToFeeImpl = ConstantMultiplier<u128, ConstU128<{ currency::WEIGHT_FEE }>>;
1
		type LengthToFeeImpl = LengthToFee;
1

            
1
		// base_fee + (multiplier * extrinsic_weight_fee) + extrinsic_length_fee + tip
1
		let expected_fee =
1
			WeightToFeeImpl::weight_to_fee(&base_extrinsic)
1
				+ multiplier.saturating_mul_int(WeightToFeeImpl::weight_to_fee(
1
					&Weight::from_parts(extrinsic_weight, 1),
1
				)) + LengthToFeeImpl::weight_to_fee(&Weight::from_parts(extrinsic_len as u64, 1))
1
				+ tip;
1

            
1
		let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
1
			.build_storage()
1
			.unwrap()
1
			.into();
1
		t.execute_with(|| {
1
			pallet_transaction_payment::NextFeeMultiplier::<Runtime>::set(multiplier);
1
			let actual_fee = TransactionPayment::compute_fee(
1
				extrinsic_len,
1
				&frame_support::dispatch::DispatchInfo {
1
					class: DispatchClass::Normal,
1
					pays_fee: frame_support::dispatch::Pays::Yes,
1
					weight: Weight::from_parts(extrinsic_weight, 1),
1
				},
1
				tip,
1
			);
1

            
1
			assert_eq!(
				expected_fee,
				actual_fee,
				"The actual fee did not match the expected fee, diff {}",
				actual_fee - expected_fee
			);
1
		});
1
	}
	#[test]
1
	fn test_min_gas_price_is_deterministic() {
1
		let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
1
			.build_storage()
1
			.unwrap()
1
			.into();
1
		t.execute_with(|| {
1
			let multiplier = sp_runtime::FixedU128::from_u32(1);
1
			pallet_transaction_payment::NextFeeMultiplier::<Runtime>::set(multiplier);
1
			let actual = TransactionPaymentAsGasPrice::min_gas_price().0;
1
			let expected: U256 = multiplier
1
				.saturating_mul_int(
1
					(currency::WEIGHT_FEE * 4).saturating_mul(WEIGHT_PER_GAS as u128),
1
				)
1
				.into();
1

            
1
			assert_eq!(expected, actual);
1
		});
1
	}
	#[test]
1
	fn test_min_gas_price_has_no_precision_loss_from_saturating_mul_int() {
1
		let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
1
			.build_storage()
1
			.unwrap()
1
			.into();
1
		t.execute_with(|| {
1
			let multiplier_1 = sp_runtime::FixedU128::from_float(0.999593900000000000);
1
			let multiplier_2 = sp_runtime::FixedU128::from_float(0.999593200000000000);
1

            
1
			pallet_transaction_payment::NextFeeMultiplier::<Runtime>::set(multiplier_1);
1
			let a = TransactionPaymentAsGasPrice::min_gas_price();
1
			pallet_transaction_payment::NextFeeMultiplier::<Runtime>::set(multiplier_2);
1
			let b = TransactionPaymentAsGasPrice::min_gas_price();
1

            
1
			assert_ne!(
				a, b,
				"both gas prices were equal, unexpected precision loss incurred"
			);
1
		});
1
	}
	#[test]
1
	fn test_fee_scenarios() {
1
		use sp_runtime::FixedU128;
1
		let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
1
			.build_storage()
1
			.unwrap()
1
			.into();
1
		t.execute_with(|| {
1
			let weight_fee_per_gas =
1
				(currency::WEIGHT_FEE * 4).saturating_mul(WEIGHT_PER_GAS as u128);
12
			let sim = |start_gas_price: u128, fullness: Perbill, num_blocks: u64| -> U256 {
12
				let start_multiplier =
12
					FixedU128::from_rational(start_gas_price, weight_fee_per_gas);
12
				pallet_transaction_payment::NextFeeMultiplier::<Runtime>::set(start_multiplier);
12

            
12
				let block_weight = NORMAL_WEIGHT * fullness;
60004
				for i in 0..num_blocks {
60004
					System::set_block_number(i as u32);
60004
					System::set_block_consumed_resources(block_weight, 0);
60004
					TransactionPayment::on_finalize(i as u32);
60004
				}
12
				TransactionPaymentAsGasPrice::min_gas_price().0
12
			};
			// The expected values are the ones observed during test execution,
			// they are expected to change when parameters that influence
			// the fee calculation are changed, and should be updated accordingly.
			// If a test fails when nothing specific to fees has changed,
			// it may indicate an unexpected collateral effect and should be investigated
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(0), 1),
1
				U256::from(125_000_000_000u128),
1
			);
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(25), 1),
1
				U256::from(125_000_000_000u128),
1
			);
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(50), 1),
1
				U256::from(125_075_022_500u128),
1
			);
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(100), 1),
1
				U256::from(125_325_422_500u128),
1
			);
			// 1 "real" hour (at 12-second blocks)
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(0), 600),
1
				U256::from(125_000_000_000u128),
1
			);
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(25), 600),
1
				U256::from(125_000_000_000u128),
1
			);
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(50), 600),
1
				U256::from(179_166_172_951u128),
1
			);
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(100), 600),
1
				U256::from(594_851_612_166u128),
1
			);
			// 1 "real" day (at 12-second blocks)
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(0), 14400),
1
				U256::from(125_000_000_000u128), // lower bound enforced
1
			);
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(25), 14400),
1
				U256::from(125_000_000_000u128),
1
			);
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(50), 14400),
1
				U256::from(706_665_861_883_635u128),
1
			);
1
			assert_eq!(
1
				sim(1_000_000_000, Perbill::from_percent(100), 14400),
1
				U256::from(12_500_000_000_000_000u128), // upper bound enforced
1
			);
1
		});
1
	}
}