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

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

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

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

            
17
//! Moonriver Runtime Integration Tests
18

            
19
#![cfg(test)]
20

            
21
mod common;
22

            
23
use common::*;
24

            
25
use fp_evm::{Context, IsPrecompileResult};
26
use frame_support::{
27
	assert_noop, assert_ok,
28
	dispatch::DispatchClass,
29
	traits::{
30
		Contains, Currency as CurrencyT, EnsureOrigin, OnInitialize, PalletInfo, StorageInfo,
31
		StorageInfoTrait,
32
	},
33
	weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight},
34
	StorageHasher, Twox128,
35
};
36
use moonriver_runtime::currency::{GIGAWEI, WEI};
37
use moonriver_runtime::runtime_params::dynamic_params;
38
use moonriver_runtime::xcm_config::{AssetHubLocation, LocationToAccountId, XcmExecutor};
39
use moonriver_runtime::{
40
	moonriver_xcm_weights,
41
	xcm_config::{CurrencyId, SelfReserve},
42
	Balances, EvmForeignAssets, Executive, NormalFilter, OpenTechCommitteeCollective, PolkadotXcm,
43
	Precompiles, ProxyType, RuntimeBlockWeights, TransactionPayment, TransactionPaymentAsGasPrice,
44
	Treasury, TreasuryCouncilCollective, XcmTransactor, WEIGHT_PER_GAS,
45
};
46
use moonriver_xcm_weights::XcmWeight;
47
use nimbus_primitives::NimbusId;
48
use pallet_evm::PrecompileSet;
49
use pallet_moonbeam_foreign_assets::AssetStatus;
50
use pallet_parachain_staking::InflationDistributionAccount;
51
use pallet_transaction_payment::Multiplier;
52
use pallet_xcm_transactor::{Currency, CurrencyPayment, TransactWeights};
53
use parity_scale_codec::Encode;
54
use polkadot_parachain::primitives::Sibling;
55
use precompile_utils::{
56
	precompile_set::{is_precompile_or_fail, IsActivePrecompile},
57
	prelude::*,
58
	testing::*,
59
};
60
use sp_core::{bounded_vec, ByteArray, Get, H160, U256};
61
use sp_runtime::{
62
	traits::{Convert, Dispatchable},
63
	BuildStorage, DispatchError, ModuleError, Percent,
64
};
65
use std::str::from_utf8;
66
use xcm::latest::prelude::*;
67
use xcm::{VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm};
68
use xcm_builder::{ParentIsPreset, SiblingParachainConvertsVia};
69
use xcm_executor::traits::{ConvertLocation, TransferType};
70
use xcm_primitives::split_location_into_chain_part_and_beneficiary;
71

            
72
type BatchPCall = pallet_evm_precompile_batch::BatchPrecompileCall<Runtime>;
73
type XcmUtilsPCall = pallet_evm_precompile_xcm_utils::XcmUtilsPrecompileCall<
74
	Runtime,
75
	moonriver_runtime::xcm_config::XcmExecutorConfig,
76
>;
77
type XcmTransactorV2PCall =
78
	pallet_evm_precompile_xcm_transactor::v2::XcmTransactorPrecompileV2Call<Runtime>;
79

            
80
const BASE_FEE_GENESIS: u128 = 100 * GIGAWEI;
81

            
82
3
fn currency_to_asset(currency_id: CurrencyId, amount: u128) -> Asset {
83
3
	Asset {
84
3
		id: AssetId(
85
3
			<moonriver_runtime::Runtime as pallet_xcm_transactor::Config>::CurrencyIdToLocation::convert(
86
3
				currency_id,
87
3
			)
88
3
			.unwrap(),
89
3
		),
90
3
		fun: Fungibility::Fungible(amount),
91
3
	}
92
3
}
93

            
94
/// Helper function to set fee per second for an asset location (for compatibility with old tests).
95
/// Converts fee_per_second to relative_price and adds/edits the asset in the weight-trader.
96
1
fn set_fee_per_second_for_location(location: Location, fee_per_second: u128) -> Result<(), ()> {
97
	use frame_support::weights::WeightToFee as _;
98
1
	let native_amount_per_second: u128 =
99
1
		<moonriver_runtime::Runtime as pallet_xcm_weight_trader::Config>::WeightToFee::weight_to_fee(
100
1
			&Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, 0),
101
		)
102
1
		.try_into()
103
1
		.map_err(|_| ())?;
104
1
	let precision_factor = 10u128.pow(pallet_xcm_weight_trader::RELATIVE_PRICE_DECIMALS);
105
1
	let relative_price: u128 = if fee_per_second > 0u128 {
106
1
		native_amount_per_second
107
1
			.saturating_mul(precision_factor)
108
1
			.saturating_div(fee_per_second)
109
	} else {
110
		0u128
111
	};
112
1
	if pallet_xcm_weight_trader::SupportedAssets::<moonriver_runtime::Runtime>::contains_key(
113
1
		&location,
114
	) {
115
1
		let enabled =
116
1
			pallet_xcm_weight_trader::SupportedAssets::<moonriver_runtime::Runtime>::get(&location)
117
1
				.ok_or(())?
118
				.0;
119
1
		pallet_xcm_weight_trader::SupportedAssets::<moonriver_runtime::Runtime>::insert(
120
1
			&location,
121
1
			(enabled, relative_price),
122
		);
123
	} else {
124
		pallet_xcm_weight_trader::SupportedAssets::<moonriver_runtime::Runtime>::insert(
125
			&location,
126
			(true, relative_price),
127
		);
128
	}
129
1
	Ok(())
130
1
}
131

            
132
#[test]
133
1
fn xcmp_queue_controller_origin_is_root() {
134
	// important for the XcmExecutionManager impl of PauseExecution which uses root origin
135
	// to suspend/resume XCM execution in xcmp_queue::on_idle
136
1
	assert_ok!(
137
1
		<moonriver_runtime::Runtime as cumulus_pallet_xcmp_queue::Config
138
1
		>::ControllerOrigin::ensure_origin(root_origin())
139
	);
140
1
}
141

            
142
#[test]
143
1
fn verify_pallet_prefixes() {
144
31
	fn is_pallet_prefix<P: 'static>(name: &str) {
145
		// Compares the unhashed pallet prefix in the `StorageInstance` implementation by every
146
		// storage item in the pallet P. This pallet prefix is used in conjunction with the
147
		// item name to get the unique storage key: hash(PalletPrefix) + hash(StorageName)
148
		// https://github.com/paritytech/substrate/blob/master/frame/support/procedural/src/pallet/
149
		// expand/storage.rs#L389-L401
150
31
		assert_eq!(
151
31
			<moonriver_runtime::Runtime as frame_system::Config>::PalletInfo::name::<P>(),
152
31
			Some(name)
153
		);
154
31
	}
155
	// TODO: use StorageInfoTrait from https://github.com/paritytech/substrate/pull/9246
156
	// This is now available with polkadot-v0.9.9 dependencies
157
1
	is_pallet_prefix::<moonriver_runtime::System>("System");
158
1
	is_pallet_prefix::<moonriver_runtime::Utility>("Utility");
159
1
	is_pallet_prefix::<moonriver_runtime::ParachainSystem>("ParachainSystem");
160
1
	is_pallet_prefix::<moonriver_runtime::TransactionPayment>("TransactionPayment");
161
1
	is_pallet_prefix::<moonriver_runtime::ParachainInfo>("ParachainInfo");
162
1
	is_pallet_prefix::<moonriver_runtime::EthereumChainId>("EthereumChainId");
163
1
	is_pallet_prefix::<moonriver_runtime::EVM>("EVM");
164
1
	is_pallet_prefix::<moonriver_runtime::Ethereum>("Ethereum");
165
1
	is_pallet_prefix::<moonriver_runtime::ParachainStaking>("ParachainStaking");
166
1
	is_pallet_prefix::<moonriver_runtime::MaintenanceMode>("MaintenanceMode");
167
1
	is_pallet_prefix::<moonriver_runtime::Scheduler>("Scheduler");
168
1
	is_pallet_prefix::<moonriver_runtime::OpenTechCommitteeCollective>(
169
1
		"OpenTechCommitteeCollective",
170
	);
171
1
	is_pallet_prefix::<moonriver_runtime::Treasury>("Treasury");
172
1
	is_pallet_prefix::<moonriver_runtime::AuthorInherent>("AuthorInherent");
173
1
	is_pallet_prefix::<moonriver_runtime::AuthorFilter>("AuthorFilter");
174
1
	is_pallet_prefix::<moonriver_runtime::CrowdloanRewards>("CrowdloanRewards");
175
1
	is_pallet_prefix::<moonriver_runtime::AuthorMapping>("AuthorMapping");
176
1
	is_pallet_prefix::<moonriver_runtime::Identity>("Identity");
177
1
	is_pallet_prefix::<moonriver_runtime::XcmpQueue>("XcmpQueue");
178
1
	is_pallet_prefix::<moonriver_runtime::CumulusXcm>("CumulusXcm");
179
1
	is_pallet_prefix::<moonriver_runtime::PolkadotXcm>("PolkadotXcm");
180
1
	is_pallet_prefix::<moonriver_runtime::XcmTransactor>("XcmTransactor");
181
1
	is_pallet_prefix::<moonriver_runtime::ProxyGenesisCompanion>("ProxyGenesisCompanion");
182
1
	is_pallet_prefix::<moonriver_runtime::MoonbeamOrbiters>("MoonbeamOrbiters");
183
1
	is_pallet_prefix::<moonriver_runtime::TreasuryCouncilCollective>("TreasuryCouncilCollective");
184
1
	is_pallet_prefix::<moonriver_runtime::MoonbeamLazyMigrations>("MoonbeamLazyMigrations");
185
1
	is_pallet_prefix::<moonriver_runtime::RelayStorageRoots>("RelayStorageRoots");
186
1
	is_pallet_prefix::<moonriver_runtime::BridgePolkadotGrandpa>("BridgePolkadotGrandpa");
187
1
	is_pallet_prefix::<moonriver_runtime::BridgePolkadotParachains>("BridgePolkadotParachains");
188
1
	is_pallet_prefix::<moonriver_runtime::BridgePolkadotMessages>("BridgePolkadotMessages");
189
1
	is_pallet_prefix::<moonriver_runtime::BridgeXcmOverMoonbeam>("BridgeXcmOverMoonbeam");
190

            
191
14
	let prefix = |pallet_name, storage_name| {
192
14
		let mut res = [0u8; 32];
193
14
		res[0..16].copy_from_slice(&Twox128::hash(pallet_name));
194
14
		res[16..32].copy_from_slice(&Twox128::hash(storage_name));
195
14
		res.to_vec()
196
14
	};
197
1
	assert_eq!(
198
1
		<moonriver_runtime::Timestamp as StorageInfoTrait>::storage_info(),
199
1
		vec![
200
1
			StorageInfo {
201
1
				pallet_name: b"Timestamp".to_vec(),
202
1
				storage_name: b"Now".to_vec(),
203
1
				prefix: prefix(b"Timestamp", b"Now"),
204
1
				max_values: Some(1),
205
1
				max_size: Some(8),
206
1
			},
207
1
			StorageInfo {
208
1
				pallet_name: b"Timestamp".to_vec(),
209
1
				storage_name: b"DidUpdate".to_vec(),
210
1
				prefix: prefix(b"Timestamp", b"DidUpdate"),
211
1
				max_values: Some(1),
212
1
				max_size: Some(1),
213
1
			}
214
		]
215
	);
216
1
	assert_eq!(
217
1
		<moonriver_runtime::Balances as StorageInfoTrait>::storage_info(),
218
1
		vec![
219
1
			StorageInfo {
220
1
				pallet_name: b"Balances".to_vec(),
221
1
				storage_name: b"TotalIssuance".to_vec(),
222
1
				prefix: prefix(b"Balances", b"TotalIssuance"),
223
1
				max_values: Some(1),
224
1
				max_size: Some(16),
225
1
			},
226
1
			StorageInfo {
227
1
				pallet_name: b"Balances".to_vec(),
228
1
				storage_name: b"InactiveIssuance".to_vec(),
229
1
				prefix: prefix(b"Balances", b"InactiveIssuance"),
230
1
				max_values: Some(1),
231
1
				max_size: Some(16),
232
1
			},
233
1
			StorageInfo {
234
1
				pallet_name: b"Balances".to_vec(),
235
1
				storage_name: b"Account".to_vec(),
236
1
				prefix: prefix(b"Balances", b"Account"),
237
1
				max_values: None,
238
1
				max_size: Some(100),
239
1
			},
240
1
			StorageInfo {
241
1
				pallet_name: b"Balances".to_vec(),
242
1
				storage_name: b"Locks".to_vec(),
243
1
				prefix: prefix(b"Balances", b"Locks"),
244
1
				max_values: None,
245
1
				max_size: Some(1287),
246
1
			},
247
1
			StorageInfo {
248
1
				pallet_name: b"Balances".to_vec(),
249
1
				storage_name: b"Reserves".to_vec(),
250
1
				prefix: prefix(b"Balances", b"Reserves"),
251
1
				max_values: None,
252
1
				max_size: Some(1037),
253
1
			},
254
1
			StorageInfo {
255
1
				pallet_name: b"Balances".to_vec(),
256
1
				storage_name: b"Holds".to_vec(),
257
1
				prefix: prefix(b"Balances", b"Holds"),
258
1
				max_values: None,
259
1
				max_size: Some(91),
260
1
			},
261
1
			StorageInfo {
262
1
				pallet_name: b"Balances".to_vec(),
263
1
				storage_name: b"Freezes".to_vec(),
264
1
				prefix: prefix(b"Balances", b"Freezes"),
265
1
				max_values: None,
266
1
				max_size: Some(73),
267
1
			},
268
		]
269
	);
270
1
	assert_eq!(
271
1
		<moonriver_runtime::Proxy as StorageInfoTrait>::storage_info(),
272
1
		vec![
273
1
			StorageInfo {
274
1
				pallet_name: b"Proxy".to_vec(),
275
1
				storage_name: b"Proxies".to_vec(),
276
1
				prefix: prefix(b"Proxy", b"Proxies"),
277
1
				max_values: None,
278
1
				max_size: Some(845),
279
1
			},
280
1
			StorageInfo {
281
1
				pallet_name: b"Proxy".to_vec(),
282
1
				storage_name: b"Announcements".to_vec(),
283
1
				prefix: prefix(b"Proxy", b"Announcements"),
284
1
				max_values: None,
285
1
				max_size: Some(1837),
286
1
			}
287
		]
288
	);
289
1
	assert_eq!(
290
1
		<moonriver_runtime::MaintenanceMode as StorageInfoTrait>::storage_info(),
291
1
		vec![StorageInfo {
292
1
			pallet_name: b"MaintenanceMode".to_vec(),
293
1
			storage_name: b"MaintenanceMode".to_vec(),
294
1
			prefix: prefix(b"MaintenanceMode", b"MaintenanceMode"),
295
1
			max_values: Some(1),
296
1
			max_size: None,
297
1
		},]
298
	);
299
1
	assert_eq!(
300
1
		<moonriver_runtime::RelayStorageRoots as StorageInfoTrait>::storage_info(),
301
1
		vec![
302
1
			StorageInfo {
303
1
				pallet_name: b"RelayStorageRoots".to_vec(),
304
1
				storage_name: b"RelayStorageRoot".to_vec(),
305
1
				prefix: prefix(b"RelayStorageRoots", b"RelayStorageRoot"),
306
1
				max_values: None,
307
1
				max_size: Some(44),
308
1
			},
309
1
			StorageInfo {
310
1
				pallet_name: b"RelayStorageRoots".to_vec(),
311
1
				storage_name: b"RelayStorageRootKeys".to_vec(),
312
1
				prefix: prefix(b"RelayStorageRoots", b"RelayStorageRootKeys"),
313
1
				max_values: Some(1),
314
1
				max_size: Some(121),
315
1
			},
316
		]
317
	);
318
1
}
319

            
320
#[test]
321
1
fn test_collectives_storage_item_prefixes() {
322
7
	for StorageInfo { pallet_name, .. } in
323
1
		<moonriver_runtime::TreasuryCouncilCollective as StorageInfoTrait>::storage_info()
324
	{
325
7
		assert_eq!(pallet_name, b"TreasuryCouncilCollective".to_vec());
326
	}
327

            
328
7
	for StorageInfo { pallet_name, .. } in
329
1
		<moonriver_runtime::OpenTechCommitteeCollective as StorageInfoTrait>::storage_info()
330
	{
331
7
		assert_eq!(pallet_name, b"OpenTechCommitteeCollective".to_vec());
332
	}
333
1
}
334

            
335
#[test]
336
1
fn collective_set_members_root_origin_works() {
337
1
	ExtBuilder::default().build().execute_with(|| {
338
		// TreasuryCouncilCollective
339
1
		assert_ok!(TreasuryCouncilCollective::set_members(
340
1
			<Runtime as frame_system::Config>::RuntimeOrigin::root(),
341
1
			vec![AccountId::from(ALICE), AccountId::from(BOB)],
342
1
			Some(AccountId::from(ALICE)),
343
			2
344
		));
345
		// OpenTechCommitteeCollective
346
1
		assert_ok!(OpenTechCommitteeCollective::set_members(
347
1
			<Runtime as frame_system::Config>::RuntimeOrigin::root(),
348
1
			vec![AccountId::from(ALICE), AccountId::from(BOB)],
349
1
			Some(AccountId::from(ALICE)),
350
			2
351
		));
352
1
	});
353
1
}
354

            
355
#[test]
356
1
fn collective_set_members_general_admin_origin_works() {
357
	use moonriver_runtime::{
358
		governance::custom_origins::Origin as CustomOrigin, OriginCaller, Utility,
359
	};
360

            
361
1
	ExtBuilder::default().build().execute_with(|| {
362
1
		let root_caller = <Runtime as frame_system::Config>::RuntimeOrigin::root();
363
1
		let alice = AccountId::from(ALICE);
364

            
365
		// TreasuryCouncilCollective
366
1
		let _ = Utility::dispatch_as(
367
1
			root_caller.clone(),
368
1
			Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)),
369
1
			Box::new(
370
1
				pallet_collective::Call::<Runtime, pallet_collective::Instance3>::set_members {
371
1
					new_members: vec![alice, AccountId::from(BOB)],
372
1
					prime: Some(alice),
373
1
					old_count: 2,
374
1
				}
375
1
				.into(),
376
1
			),
377
1
		);
378
		// OpenTechCommitteeCollective
379
1
		let _ = Utility::dispatch_as(
380
1
			root_caller,
381
1
			Box::new(OriginCaller::Origins(CustomOrigin::GeneralAdmin)),
382
1
			Box::new(
383
1
				pallet_collective::Call::<Runtime, pallet_collective::Instance4>::set_members {
384
1
					new_members: vec![alice, AccountId::from(BOB)],
385
1
					prime: Some(alice),
386
1
					old_count: 2,
387
1
				}
388
1
				.into(),
389
1
			),
390
1
		);
391

            
392
1
		assert_eq!(
393
1
			System::events()
394
1
				.into_iter()
395
2
				.filter_map(|r| {
396
2
					match r.event {
397
2
						RuntimeEvent::Utility(pallet_utility::Event::DispatchedAs { result })
398
2
							if result.is_ok() =>
399
						{
400
2
							Some(true)
401
						}
402
						_ => None,
403
					}
404
2
				})
405
1
				.collect::<Vec<_>>()
406
1
				.len(),
407
			2
408
		)
409
1
	});
410
1
}
411

            
412
#[test]
413
1
fn collective_set_members_signed_origin_does_not_work() {
414
1
	let alice = AccountId::from(ALICE);
415
1
	ExtBuilder::default().build().execute_with(|| {
416
		// TreasuryCouncilCollective
417
1
		assert!(TreasuryCouncilCollective::set_members(
418
1
			<Runtime as frame_system::Config>::RuntimeOrigin::signed(alice),
419
1
			vec![AccountId::from(ALICE), AccountId::from(BOB)],
420
1
			Some(AccountId::from(ALICE)),
421
1
			2
422
1
		)
423
1
		.is_err());
424
		// OpenTechCommitteeCollective
425
1
		assert!(OpenTechCommitteeCollective::set_members(
426
1
			<Runtime as frame_system::Config>::RuntimeOrigin::signed(alice),
427
1
			vec![AccountId::from(ALICE), AccountId::from(BOB)],
428
1
			Some(AccountId::from(ALICE)),
429
1
			2
430
1
		)
431
1
		.is_err());
432
1
	});
433
1
}
434

            
435
#[test]
436
1
fn verify_pallet_indices() {
437
33
	fn is_pallet_index<P: 'static>(index: usize) {
438
33
		assert_eq!(
439
33
			<moonriver_runtime::Runtime as frame_system::Config>::PalletInfo::index::<P>(),
440
33
			Some(index)
441
		);
442
33
	}
443
	// System support
444
1
	is_pallet_index::<moonriver_runtime::System>(0);
445
1
	is_pallet_index::<moonriver_runtime::ParachainSystem>(1);
446
1
	is_pallet_index::<moonriver_runtime::Timestamp>(3);
447
1
	is_pallet_index::<moonriver_runtime::ParachainInfo>(4);
448
	// Monetary
449
1
	is_pallet_index::<moonriver_runtime::Balances>(10);
450
1
	is_pallet_index::<moonriver_runtime::TransactionPayment>(11);
451
	// Consensus support
452
1
	is_pallet_index::<moonriver_runtime::ParachainStaking>(20);
453
1
	is_pallet_index::<moonriver_runtime::AuthorInherent>(21);
454
1
	is_pallet_index::<moonriver_runtime::AuthorFilter>(22);
455
1
	is_pallet_index::<moonriver_runtime::AuthorMapping>(23);
456
1
	is_pallet_index::<moonriver_runtime::MoonbeamOrbiters>(24);
457
	// Handy utilities
458
1
	is_pallet_index::<moonriver_runtime::Utility>(30);
459
1
	is_pallet_index::<moonriver_runtime::Proxy>(31);
460
1
	is_pallet_index::<moonriver_runtime::MaintenanceMode>(32);
461
1
	is_pallet_index::<moonriver_runtime::Identity>(33);
462
1
	is_pallet_index::<moonriver_runtime::ProxyGenesisCompanion>(35);
463
1
	is_pallet_index::<moonriver_runtime::MoonbeamLazyMigrations>(37);
464
	// Ethereum compatibility
465
1
	is_pallet_index::<moonriver_runtime::EthereumChainId>(50);
466
1
	is_pallet_index::<moonriver_runtime::EVM>(51);
467
1
	is_pallet_index::<moonriver_runtime::Ethereum>(52);
468
	// Governance
469
1
	is_pallet_index::<moonriver_runtime::Scheduler>(60);
470
	// is_pallet_index::<moonriver_runtime::Democracy>(61); Removed
471
	// Council
472
	// is_pallet_index::<moonriver_runtime::CouncilCollective>(70); Removed
473
	// is_pallet_index::<moonriver_runtime::TechCommitteeCollective>(71); Removed
474
1
	is_pallet_index::<moonriver_runtime::TreasuryCouncilCollective>(72);
475
1
	is_pallet_index::<moonriver_runtime::OpenTechCommitteeCollective>(73);
476
	// Treasury
477
1
	is_pallet_index::<moonriver_runtime::Treasury>(80);
478
	// Crowdloan
479
1
	is_pallet_index::<moonriver_runtime::CrowdloanRewards>(90);
480
	// XCM Stuff
481
1
	is_pallet_index::<moonriver_runtime::XcmpQueue>(100);
482
1
	is_pallet_index::<moonriver_runtime::CumulusXcm>(101);
483
1
	is_pallet_index::<moonriver_runtime::PolkadotXcm>(103);
484
	// is_pallet_index::<moonriver_runtime::Assets>(104); Removed
485
	// is_pallet_index::<moonriver_runtime::AssetManager>(105); Removed
486
	// is_pallet_index::<moonriver_runtime::XTokens>(106); Removed
487
1
	is_pallet_index::<moonriver_runtime::XcmTransactor>(107);
488
1
	is_pallet_index::<moonriver_runtime::BridgePolkadotGrandpa>(130);
489
1
	is_pallet_index::<moonriver_runtime::BridgePolkadotParachains>(131);
490
1
	is_pallet_index::<moonriver_runtime::BridgePolkadotMessages>(132);
491
1
	is_pallet_index::<moonriver_runtime::BridgeXcmOverMoonbeam>(133);
492
1
}
493

            
494
#[test]
495
1
fn verify_reserved_indices() {
496
1
	let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
497
1
		.build_storage()
498
1
		.unwrap()
499
1
		.into();
500

            
501
1
	t.execute_with(|| {
502
		use frame_metadata::*;
503
1
		let metadata = moonriver_runtime::Runtime::metadata();
504
1
		let metadata = match metadata.1 {
505
1
			RuntimeMetadata::V14(metadata) => metadata,
506
			_ => panic!("metadata has been bumped, test needs to be updated"),
507
		};
508
		// 40: Sudo
509
		// 53: BaseFee
510
		// 108: pallet_assets::<Instance1>
511
1
		let reserved = vec![40, 53, 108];
512
1
		let existing = metadata
513
1
			.pallets
514
1
			.iter()
515
1
			.map(|p| p.index)
516
1
			.collect::<Vec<u8>>();
517
3
		assert!(reserved.iter().all(|index| !existing.contains(index)));
518
1
	});
519
1
}
520

            
521
#[test]
522
1
fn verify_proxy_type_indices() {
523
1
	assert_eq!(moonriver_runtime::ProxyType::Any as u8, 0);
524
1
	assert_eq!(moonriver_runtime::ProxyType::NonTransfer as u8, 1);
525
1
	assert_eq!(moonriver_runtime::ProxyType::Governance as u8, 2);
526
1
	assert_eq!(moonriver_runtime::ProxyType::Staking as u8, 3);
527
1
	assert_eq!(moonriver_runtime::ProxyType::CancelProxy as u8, 4);
528
1
	assert_eq!(moonriver_runtime::ProxyType::Balances as u8, 5);
529
1
	assert_eq!(moonriver_runtime::ProxyType::AuthorMapping as u8, 6);
530
1
	assert_eq!(moonriver_runtime::ProxyType::IdentityJudgement as u8, 7);
531
1
}
532

            
533
// This test ensure that we not filter out pure proxy calls
534
#[test]
535
1
fn verify_normal_filter_allow_pure_proxy() {
536
1
	ExtBuilder::default().build().execute_with(|| {
537
1
		assert!(NormalFilter::contains(&RuntimeCall::Proxy(
538
1
			pallet_proxy::Call::<Runtime>::create_pure {
539
1
				proxy_type: ProxyType::Any,
540
1
				delay: 0,
541
1
				index: 0,
542
1
			}
543
1
		)));
544
1
		assert!(NormalFilter::contains(&RuntimeCall::Proxy(
545
1
			pallet_proxy::Call::<Runtime>::kill_pure {
546
1
				spawner: AccountId::from(ALICE),
547
1
				proxy_type: ProxyType::Any,
548
1
				index: 0,
549
1
				height: 0,
550
1
				ext_index: 0,
551
1
			}
552
1
		)));
553
1
	});
554
1
}
555

            
556
#[test]
557
1
fn join_collator_candidates() {
558
1
	ExtBuilder::default()
559
1
		.with_balances(vec![
560
1
			(AccountId::from(ALICE), 20_000 * MOVR),
561
1
			(AccountId::from(BOB), 20_000 * MOVR),
562
1
			(AccountId::from(CHARLIE), 10_100 * MOVR),
563
1
			(AccountId::from(DAVE), 10_000 * MOVR),
564
1
		])
565
1
		.with_collators(vec![
566
1
			(AccountId::from(ALICE), 10_000 * MOVR),
567
1
			(AccountId::from(BOB), 10_000 * MOVR),
568
1
		])
569
1
		.with_delegations(vec![
570
1
			(AccountId::from(CHARLIE), AccountId::from(ALICE), 50 * MOVR),
571
1
			(AccountId::from(CHARLIE), AccountId::from(BOB), 50 * MOVR),
572
1
		])
573
1
		.build()
574
1
		.execute_with(|| {
575
1
			assert_noop!(
576
1
				ParachainStaking::join_candidates(
577
1
					origin_of(AccountId::from(ALICE)),
578
1
					10_000 * MOVR,
579
					2u32
580
				),
581
1
				pallet_parachain_staking::Error::<Runtime>::CandidateExists
582
			);
583
1
			assert_noop!(
584
1
				ParachainStaking::join_candidates(
585
1
					origin_of(AccountId::from(CHARLIE)),
586
1
					10_000 * MOVR,
587
					2u32
588
				),
589
1
				pallet_parachain_staking::Error::<Runtime>::DelegatorExists
590
			);
591
1
			assert!(System::events().is_empty());
592
1
			assert_ok!(ParachainStaking::join_candidates(
593
1
				origin_of(AccountId::from(DAVE)),
594
1
				10_000 * MOVR,
595
				2u32
596
			));
597
1
			assert_eq!(
598
1
				last_event(),
599
1
				RuntimeEvent::ParachainStaking(
600
1
					pallet_parachain_staking::Event::JoinedCollatorCandidates {
601
1
						account: AccountId::from(DAVE),
602
1
						amount_locked: 10_000 * MOVR,
603
1
						new_total_amt_locked: 30_100 * MOVR
604
1
					}
605
1
				)
606
			);
607
1
			let candidates = ParachainStaking::candidate_pool();
608
1
			assert_eq!(candidates.0[0].owner, AccountId::from(ALICE));
609
1
			assert_eq!(candidates.0[0].amount, 10_050 * MOVR);
610
1
			assert_eq!(candidates.0[1].owner, AccountId::from(BOB));
611
1
			assert_eq!(candidates.0[1].amount, 10_050 * MOVR);
612
1
			assert_eq!(candidates.0[2].owner, AccountId::from(DAVE));
613
1
			assert_eq!(candidates.0[2].amount, 10_000 * MOVR);
614
1
		});
615
1
}
616

            
617
#[test]
618
1
fn transfer_through_evm_to_stake() {
619
1
	ExtBuilder::default()
620
1
		.with_balances(vec![(AccountId::from(ALICE), 20_000 * MOVR)])
621
1
		.build()
622
1
		.execute_with(|| {
623
			// Charlie has no balance => fails to stake
624
1
			assert_noop!(
625
1
				ParachainStaking::join_candidates(
626
1
					origin_of(AccountId::from(CHARLIE)),
627
1
					10_000 * MOVR,
628
					2u32
629
				),
630
1
				DispatchError::Module(ModuleError {
631
1
					index: 20,
632
1
					error: [8, 0, 0, 0],
633
1
					message: Some("InsufficientBalance")
634
1
				})
635
			);
636
			// Alice transfer from free balance 20000 MOVR to Bob
637
1
			assert_ok!(Balances::transfer_allow_death(
638
1
				origin_of(AccountId::from(ALICE)),
639
1
				AccountId::from(BOB),
640
1
				20_000 * MOVR,
641
			));
642
1
			assert_eq!(Balances::free_balance(AccountId::from(BOB)), 20_000 * MOVR);
643

            
644
1
			let gas_limit = 100000u64;
645
1
			let gas_price: U256 = BASE_FEE_GENESIS.into();
646
			// Bob transfers 10000 MOVR to Charlie via EVM
647
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
648
1
				source: H160::from(BOB),
649
1
				target: H160::from(CHARLIE),
650
1
				input: vec![],
651
1
				value: (10_000 * MOVR).into(),
652
1
				gas_limit,
653
1
				max_fee_per_gas: gas_price,
654
1
				max_priority_fee_per_gas: None,
655
1
				nonce: None,
656
1
				access_list: Vec::new(),
657
1
				authorization_list: Vec::new(),
658
1
			})
659
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
660
1
			assert_eq!(
661
1
				Balances::free_balance(AccountId::from(CHARLIE)),
662
				10_000 * MOVR,
663
			);
664

            
665
			// Charlie can stake now
666
1
			assert_ok!(ParachainStaking::join_candidates(
667
1
				origin_of(AccountId::from(CHARLIE)),
668
1
				10_000 * MOVR,
669
				2u32,
670
			),);
671
1
			let candidates = ParachainStaking::candidate_pool();
672
1
			assert_eq!(candidates.0[0].owner, AccountId::from(CHARLIE));
673
1
			assert_eq!(candidates.0[0].amount, 10_000 * MOVR);
674
1
		});
675
1
}
676

            
677
#[test]
678
1
fn reward_block_authors() {
679
1
	ExtBuilder::default()
680
1
		.with_balances(vec![
681
1
			// Alice gets 100 extra tokens for her mapping deposit
682
1
			(AccountId::from(ALICE), 20_100 * MOVR),
683
1
			(AccountId::from(BOB), 10_000 * MOVR),
684
1
		])
685
1
		.with_collators(vec![(AccountId::from(ALICE), 10_000 * MOVR)])
686
1
		.with_delegations(vec![(
687
1
			AccountId::from(BOB),
688
1
			AccountId::from(ALICE),
689
1
			500 * MOVR,
690
1
		)])
691
1
		.with_mappings(vec![(
692
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
693
1
			AccountId::from(ALICE),
694
1
		)])
695
1
		.build()
696
1
		.execute_with(|| {
697
1
			increase_last_relay_slot_number(1);
698

            
699
			// Just before round 3
700
1
			run_to_block(2399, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
701

            
702
			// no rewards doled out yet
703
1
			assert_eq!(
704
1
				Balances::usable_balance(AccountId::from(ALICE)),
705
				10_100 * MOVR,
706
			);
707
1
			assert_eq!(Balances::usable_balance(AccountId::from(BOB)), 9500 * MOVR,);
708
1
			run_to_block(2401, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
709

            
710
			// rewards minted and distributed
711
1
			assert_eq!(
712
1
				Balances::usable_balance(AccountId::from(ALICE)),
713
				11547666666208000000000,
714
			);
715
1
			assert_eq!(
716
1
				Balances::usable_balance(AccountId::from(BOB)),
717
				9557333332588000000000,
718
			);
719
1
		});
720
1
}
721

            
722
#[test]
723
1
fn reward_block_authors_with_parachain_bond_reserved() {
724
1
	ExtBuilder::default()
725
1
		.with_balances(vec![
726
1
			// Alice gets 100 extra tokens for her mapping deposit
727
1
			(AccountId::from(ALICE), 20_100 * MOVR),
728
1
			(AccountId::from(BOB), 10_000 * MOVR),
729
1
			(AccountId::from(CHARLIE), MOVR),
730
1
		])
731
1
		.with_collators(vec![(AccountId::from(ALICE), 10_000 * MOVR)])
732
1
		.with_delegations(vec![(
733
1
			AccountId::from(BOB),
734
1
			AccountId::from(ALICE),
735
1
			500 * MOVR,
736
1
		)])
737
1
		.with_mappings(vec![(
738
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
739
1
			AccountId::from(ALICE),
740
1
		)])
741
1
		.build()
742
1
		.execute_with(|| {
743
1
			increase_last_relay_slot_number(1);
744
1
			assert_ok!(ParachainStaking::set_inflation_distribution_config(
745
1
				root_origin(),
746
1
				[
747
1
					InflationDistributionAccount {
748
1
						account: AccountId::from(CHARLIE),
749
1
						percent: Percent::from_percent(30),
750
1
					},
751
1
					InflationDistributionAccount::default(),
752
1
				]
753
1
				.into()
754
			));
755

            
756
			// Stop just before round 2
757
1
			run_to_block(1199, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
758

            
759
			// no collator rewards doled out yet
760
1
			assert_eq!(
761
1
				Balances::usable_balance(AccountId::from(ALICE)),
762
				10_100 * MOVR,
763
			);
764
1
			assert_eq!(Balances::usable_balance(AccountId::from(BOB)), 9500 * MOVR,);
765

            
766
			// Go to round 2
767
1
			run_to_block(1201, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
768

            
769
			// 30% reserved for parachain bond
770
1
			assert_eq!(
771
1
				Balances::usable_balance(AccountId::from(CHARLIE)),
772
				452515000000000000000,
773
			);
774

            
775
			// Go to round 3
776
1
			run_to_block(2401, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
777

            
778
			// rewards minted and distributed
779
1
			assert_eq!(
780
1
				Balances::usable_balance(AccountId::from(ALICE)),
781
				11117700475903800000000,
782
			);
783
1
			assert_eq!(
784
1
				Balances::usable_balance(AccountId::from(BOB)),
785
				9535834523343675000000,
786
			);
787
			// 30% reserved for parachain bond again
788
1
			assert_eq!(
789
1
				Balances::usable_balance(AccountId::from(CHARLIE)),
790
				910802725000000000000,
791
			);
792
1
		});
793
1
}
794

            
795
1
fn run_with_system_weight<F>(w: Weight, mut assertions: F)
796
1
where
797
1
	F: FnMut() -> (),
798
{
799
1
	let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
800
1
		.build_storage()
801
1
		.unwrap()
802
1
		.into();
803
1
	t.execute_with(|| {
804
1
		System::set_block_consumed_resources(w, 0);
805
1
		assertions()
806
1
	});
807
1
}
808

            
809
#[test]
810
#[rustfmt::skip]
811
1
fn length_fee_is_sensible() {
812
	use sp_runtime::testing::TestXt;
813

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

            
819
9
		let calc_fee = |len: u32| -> Balance {
820
9
			moonriver_runtime::TransactionPayment::query_fee_details(uxt.clone(), len)
821
9
				.inclusion_fee
822
9
				.expect("fee should be calculated")
823
9
				.len_fee
824
9
		};
825

            
826
		//                  left: cost of length fee, right: size in bytes
827
		//                             /------------- proportional component: O(N * 1B)
828
		//                             |           /- exponential component: O(N ** 3)
829
		//                             |           |
830
1
		assert_eq!(                    1_000_000_001, calc_fee(1));
831
1
		assert_eq!(                   10_000_001_000, calc_fee(10));
832
1
		assert_eq!(                  100_001_000_000, calc_fee(100));
833
1
		assert_eq!(                1_001_000_000_000, calc_fee(1_000));
834
1
		assert_eq!(               11_000_000_000_000, calc_fee(10_000)); // inflection point
835
1
		assert_eq!(            1_100_000_000_000_000, calc_fee(100_000));
836
1
		assert_eq!(        1_001_000_000_000_000_000, calc_fee(1_000_000)); // one MOVR, ~ 1MB
837
1
		assert_eq!(    1_000_010_000_000_000_000_000, calc_fee(10_000_000));
838
1
		assert_eq!(1_000_000_100_000_000_000_000_000, calc_fee(100_000_000));
839
1
	});
840
1
}
841

            
842
#[test]
843
1
fn multiplier_can_grow_from_zero() {
844
	use frame_support::traits::Get;
845

            
846
1
	let minimum_multiplier = moonriver_runtime::MinimumMultiplier::get();
847
1
	let target = moonriver_runtime::TargetBlockFullness::get()
848
1
		* RuntimeBlockWeights::get()
849
1
			.get(DispatchClass::Normal)
850
1
			.max_total
851
1
			.unwrap();
852
	// if the min is too small, then this will not change, and we are doomed forever.
853
	// the weight is 1/100th bigger than target.
854
1
	run_with_system_weight(target * 101 / 100, || {
855
1
		let next =
856
1
			moonriver_runtime::SlowAdjustingFeeUpdate::<Runtime>::convert(minimum_multiplier);
857
1
		assert!(
858
1
			next > minimum_multiplier,
859
			"{:?} !>= {:?}",
860
			next,
861
			minimum_multiplier
862
		);
863
1
	})
864
1
}
865

            
866
#[test]
867
1
fn ethereum_invalid_transaction() {
868
1
	ExtBuilder::default().build().execute_with(|| {
869
1
		set_parachain_inherent_data();
870
		// Ensure an extrinsic not containing enough gas limit to store the transaction
871
		// on chain is rejected.
872
1
		assert_eq!(
873
1
			Executive::apply_extrinsic(unchecked_eth_tx(INVALID_ETH_TX)),
874
			Err(
875
				sp_runtime::transaction_validity::TransactionValidityError::Invalid(
876
					sp_runtime::transaction_validity::InvalidTransaction::Custom(0u8)
877
				)
878
			)
879
		);
880
1
	});
881
1
}
882

            
883
#[test]
884
1
fn initial_gas_fee_is_correct() {
885
	use fp_evm::FeeCalculator;
886

            
887
1
	ExtBuilder::default().build().execute_with(|| {
888
1
		let multiplier = TransactionPayment::next_fee_multiplier();
889
1
		assert_eq!(multiplier, Multiplier::from(10u128));
890

            
891
1
		assert_eq!(
892
1
			TransactionPaymentAsGasPrice::min_gas_price(),
893
1
			(
894
1
				3_125_000_000u128.into(),
895
1
				Weight::from_parts(<Runtime as frame_system::Config>::DbWeight::get().read, 0)
896
1
			)
897
		);
898
1
	});
899
1
}
900

            
901
#[test]
902
1
fn min_gas_fee_is_correct() {
903
	use fp_evm::FeeCalculator;
904
	use frame_support::traits::Hooks;
905

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

            
910
1
		let multiplier = TransactionPayment::next_fee_multiplier();
911
1
		assert_eq!(multiplier, Multiplier::from(1u128));
912

            
913
1
		assert_eq!(
914
1
			TransactionPaymentAsGasPrice::min_gas_price(),
915
1
			(
916
1
				312_500_000u128.into(),
917
1
				Weight::from_parts(<Runtime as frame_system::Config>::DbWeight::get().read, 0)
918
1
			)
919
		);
920
1
	});
921
1
}
922

            
923
#[test]
924
1
fn transfer_ed_0_substrate() {
925
1
	ExtBuilder::default()
926
1
		.with_balances(vec![
927
1
			(AccountId::from(ALICE), (1 * MOVR) + (1 * WEI)),
928
1
			(AccountId::from(BOB), existential_deposit()),
929
1
		])
930
1
		.build()
931
1
		.execute_with(|| {
932
			// Substrate transfer
933
1
			assert_ok!(Balances::transfer_allow_death(
934
1
				origin_of(AccountId::from(ALICE)),
935
1
				AccountId::from(BOB),
936
1
				1 * MOVR,
937
			));
938
			// 1 WEI is left in the account
939
1
			assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI);
940
1
		});
941
1
}
942

            
943
#[test]
944
1
fn transfer_ed_0_evm() {
945
1
	ExtBuilder::default()
946
1
		.with_balances(vec![
947
1
			(
948
1
				AccountId::from(ALICE),
949
1
				((1 * MOVR) + (21_000 * BASE_FEE_GENESIS)) + (1 * WEI),
950
1
			),
951
1
			(AccountId::from(BOB), existential_deposit()),
952
1
		])
953
1
		.build()
954
1
		.execute_with(|| {
955
1
			set_parachain_inherent_data();
956
			// EVM transfer
957
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
958
1
				source: H160::from(ALICE),
959
1
				target: H160::from(BOB),
960
1
				input: Vec::new(),
961
1
				value: (1 * MOVR).into(),
962
1
				gas_limit: 21_000u64,
963
1
				max_fee_per_gas: U256::from(BASE_FEE_GENESIS),
964
1
				max_priority_fee_per_gas: Some(U256::from(BASE_FEE_GENESIS)),
965
1
				nonce: Some(U256::from(0)),
966
1
				access_list: Vec::new(),
967
1
				authorization_list: Vec::new(),
968
1
			})
969
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
970
			// 1 WEI is left in the account
971
1
			assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 1 * WEI,);
972
1
		});
973
1
}
974

            
975
#[test]
976
1
fn refund_ed_0_evm() {
977
1
	ExtBuilder::default()
978
1
		.with_balances(vec![
979
1
			(
980
1
				AccountId::from(ALICE),
981
1
				((1 * MOVR) + (21_777 * BASE_FEE_GENESIS) + existential_deposit()),
982
1
			),
983
1
			(AccountId::from(BOB), existential_deposit()),
984
1
		])
985
1
		.build()
986
1
		.execute_with(|| {
987
1
			set_parachain_inherent_data();
988
			// EVM transfer that zeroes ALICE
989
1
			assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
990
1
				source: H160::from(ALICE),
991
1
				target: H160::from(BOB),
992
1
				input: Vec::new(),
993
1
				value: (1 * MOVR).into(),
994
1
				gas_limit: 21_777u64,
995
1
				max_fee_per_gas: U256::from(BASE_FEE_GENESIS),
996
1
				max_priority_fee_per_gas: Some(U256::from(BASE_FEE_GENESIS)),
997
1
				nonce: Some(U256::from(0)),
998
1
				access_list: Vec::new(),
999
1
				authorization_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 + existential_deposit(),
			);
1
		});
1
}
#[test]
1
fn author_does_receive_priority_fee() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(
1
			AccountId::from(BOB),
1
			(1 * MOVR) + (21_000 * (500 * GIGAWEI)),
1
		)])
1
		.build()
1
		.execute_with(|| {
			// Some block author as seen by pallet-evm.
1
			let author = AccountId::from(<pallet_evm::Pallet<Runtime>>::find_author());
1
			pallet_author_inherent::Author::<Runtime>::put(author);
			// Currently the default impl of the evm uses `deposit_into_existing`.
			// If we were to use this implementation, and for an author to receive eventual tips,
			// the account needs to be somehow initialized, otherwise the deposit would fail.
1
			Balances::make_free_balance_be(&author, 100 * MOVR);
			// 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 * MOVR).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
				authorization_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
1
			let priority_fee = 200 * GIGAWEI * 21_000;
			// Author free balance increased by priority fee.
1
			assert_eq!(Balances::free_balance(author), 100 * MOVR + priority_fee,);
1
		});
1
}
#[test]
1
fn total_issuance_after_evm_transaction_with_priority_fee() {
	use fp_evm::FeeCalculator;
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(
1
				AccountId::from(BOB),
1
				(1 * MOVR) + (21_000 * (2 * BASE_FEE_GENESIS) + existential_deposit()),
1
			),
1
			(
1
				<pallet_treasury::TreasuryAccountId<Runtime> as sp_core::TypedGet>::get(),
1
				existential_deposit(),
1
			),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			let issuance_before = <Runtime as pallet_evm::Config>::Currency::total_issuance();
1
			let author = AccountId::from(<pallet_evm::Pallet<Runtime>>::find_author());
1
			pallet_author_inherent::Author::<Runtime>::put(author);
			// 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 * MOVR).into(),
1
				gas_limit: 21_000u64,
1
				max_fee_per_gas: U256::from(2u128 * BASE_FEE_GENESIS),
1
				max_priority_fee_per_gas: Some(U256::from(2u128 * BASE_FEE_GENESIS)),
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
				authorization_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
			let base_fee = TransactionPaymentAsGasPrice::min_gas_price().0.as_u128();
1
			let base_fee: Balance = base_fee * 21_000;
1
			let treasury_proportion = dynamic_params::runtime_config::FeesTreasuryProportion::get();
			// only base fee is split between being burned and sent to treasury
1
			let treasury_base_fee_part: Balance = treasury_proportion.mul_floor(base_fee);
1
			let burnt_base_fee_part: Balance = base_fee - treasury_base_fee_part;
1
			assert_eq!(issuance_after, issuance_before - burnt_base_fee_part);
1
			assert_eq!(moonriver_runtime::Treasury::pot(), treasury_base_fee_part);
1
		});
1
}
#[test]
1
fn total_issuance_after_evm_transaction_without_priority_fee() {
	use fp_evm::FeeCalculator;
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(
1
				AccountId::from(BOB),
1
				(1 * MOVR) + (21_000 * (2 * BASE_FEE_GENESIS) + existential_deposit()),
1
			),
1
			(
1
				<pallet_treasury::TreasuryAccountId<Runtime> as sp_core::TypedGet>::get(),
1
				existential_deposit(),
1
			),
1
		])
1
		.build()
1
		.execute_with(|| {
1
			let issuance_before = <Runtime as pallet_evm::Config>::Currency::total_issuance();
			// 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 * MOVR).into(),
1
				gas_limit: 21_000u64,
1
				max_fee_per_gas: U256::from(BASE_FEE_GENESIS),
1
				max_priority_fee_per_gas: None,
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
				authorization_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
			let base_fee = TransactionPaymentAsGasPrice::min_gas_price().0.as_u128();
1
			let base_fee: Balance = base_fee * 21_000;
1
			let treasury_proportion = dynamic_params::runtime_config::FeesTreasuryProportion::get();
			// only base fee is split between being burned and sent to treasury
1
			let treasury_base_fee_part: Balance = treasury_proportion.mul_floor(base_fee);
1
			let burnt_base_fee_part: Balance = base_fee - treasury_base_fee_part;
1
			assert_eq!(issuance_after, issuance_before - burnt_base_fee_part);
1
			assert_eq!(moonriver_runtime::Treasury::pot(), treasury_base_fee_part);
1
		});
1
}
#[test]
1
fn root_can_change_default_xcm_vers() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * MOVR),
1
			(AccountId::from(BOB), 1_000 * MOVR),
1
		])
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_id: 1,
1
			xcm_location: AssetHubLocation::get(),
1
			name: "Dot",
1
			symbol: "Dot",
1
			decimals: 12,
1
			balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)],
1
		}])
1
		.build()
1
		.execute_with(|| {
1
			let source_id: moonriver_runtime::AssetId = 1;
1
			let currency_id = moonriver_runtime::xcm_config::CurrencyId::ForeignAsset(source_id);
1
			let asset = Asset {
1
				id: AssetId(
1
					<Runtime as pallet_xcm_transactor::Config>::CurrencyIdToLocation::convert(
1
						currency_id,
1
					)
1
					.unwrap(),
1
				),
1
				fun: Fungibility::Fungible(100_000_000_000_000),
1
			};
			// Default XCM version is not set yet, so xtokens should fail because it does not
			// know with which version to send
1
			assert_noop!(
1
				PolkadotXcm::transfer_assets(
1
					origin_of(AccountId::from(ALICE)),
1
					Box::new(VersionedLocation::from(Location::parent())),
1
					Box::new(VersionedLocation::from(Location {
1
						parents: 0,
1
						interior: [AccountId32 {
1
							network: None,
1
							id: [1u8; 32],
1
						}]
1
						.into(),
1
					})),
1
					Box::new(VersionedAssets::from(asset.clone())),
					0,
1
					WeightLimit::Unlimited,
				),
1
				pallet_xcm::Error::<Runtime>::LocalExecutionIncompleteWithError {
1
					index: 2,
1
					error: pallet_xcm::ExecutionError::DestinationUnsupported
1
				}
			);
			// Root sets the defaultXcm
1
			assert_ok!(PolkadotXcm::force_default_xcm_version(
1
				root_origin(),
1
				Some(3)
			));
			// Now transferring does not fail
1
			assert_ok!(PolkadotXcm::transfer_assets(
1
				origin_of(AccountId::from(ALICE)),
1
				Box::new(VersionedLocation::from(Location::parent())),
1
				Box::new(VersionedLocation::from(Location {
1
					parents: 0,
1
					interior: [AccountId32 {
1
						network: None,
1
						id: [1u8; 32],
1
					}]
1
					.into(),
1
				})),
1
				Box::new(VersionedAssets::from(asset)),
				0,
1
				WeightLimit::Unlimited
			));
1
		})
1
}
#[test]
1
fn asset_can_be_registered() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		let source_location = Location::parent();
1
		let source_id = 1;
1
		assert_ok!(EvmForeignAssets::create_foreign_asset(
1
			moonriver_runtime::RuntimeOrigin::root(),
1
			source_id,
1
			source_location.clone(),
			12,
1
			b"Relay".to_vec().try_into().unwrap(),
1
			b"RelayToken".to_vec().try_into().unwrap(),
		));
		// Check that the asset was created
		// First check if the asset ID exists
1
		let location = EvmForeignAssets::assets_by_id(source_id).expect("Asset should exist");
1
		assert_eq!(location.clone(), source_location);
		// Then check the status using AssetsByLocation
1
		let (asset_id, status) =
1
			EvmForeignAssets::assets_by_location(&location).expect("Asset location should exist");
1
		assert_eq!(asset_id, source_id);
1
		assert_eq!(status, AssetStatus::Active);
1
	});
1
}
#[test]
1
fn create_and_manipulate_foreign_asset_using_root() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		let source_location = Location::parent();
		// Create foreign asset
1
		assert_ok!(EvmForeignAssets::create_foreign_asset(
1
			moonriver_runtime::RuntimeOrigin::root(),
			1,
1
			source_location.clone(),
			12,
1
			bounded_vec![b'M', b'T'],
1
			bounded_vec![b'M', b'y', b'T', b'o', b'k'],
		));
1
		assert_eq!(
1
			EvmForeignAssets::assets_by_id(1),
1
			Some(source_location.clone())
		);
1
		assert_eq!(
1
			EvmForeignAssets::assets_by_location(&source_location),
			Some((1, AssetStatus::Active))
		);
		// Freeze foreign asset
1
		assert_ok!(EvmForeignAssets::freeze_foreign_asset(
1
			moonriver_runtime::RuntimeOrigin::root(),
			1,
			true
		));
1
		assert_eq!(
1
			EvmForeignAssets::assets_by_location(&source_location),
			Some((1, AssetStatus::FrozenXcmDepositAllowed))
		);
		// Unfreeze foreign asset
1
		assert_ok!(EvmForeignAssets::unfreeze_foreign_asset(
1
			moonriver_runtime::RuntimeOrigin::root(),
			1,
		));
1
		assert_eq!(
1
			EvmForeignAssets::assets_by_location(&source_location),
			Some((1, AssetStatus::Active))
		);
1
	});
1
}
#[test]
1
fn create_and_manipulate_foreign_asset_using_sibling() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		let asset_location: Location = (Parent, Parachain(1), PalletInstance(3)).into();
1
		let para_location = asset_location.chain_location();
1
		let para_account =
1
			LocationToAccountId::convert_location(&para_location).expect("Cannot convert location");
1
		let deposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit::get();
1
		Balances::make_free_balance_be(&para_account, deposit * 2);
		// Create foreign asset
1
		assert_ok!(EvmForeignAssets::create_foreign_asset(
1
			pallet_xcm::Origin::Xcm(para_location.clone()).into(),
			1,
1
			asset_location.clone(),
			12,
1
			bounded_vec![b'M', b'T'],
1
			bounded_vec![b'M', b'y', b'T', b'o', b'k'],
		));
		// deposit is taken from the account
1
		assert_eq!(Balances::free_balance(&para_account), deposit);
1
		assert_eq!(
1
			EvmForeignAssets::assets_by_id(1),
1
			Some(asset_location.clone())
		);
1
		assert_eq!(
1
			EvmForeignAssets::assets_by_location(&asset_location),
			Some((1, AssetStatus::Active))
		);
		// Freeze foreign asset
1
		assert_ok!(EvmForeignAssets::freeze_foreign_asset(
1
			pallet_xcm::Origin::Xcm(para_location.clone()).into(),
			1,
			true
		));
1
		assert_eq!(
1
			EvmForeignAssets::assets_by_location(&asset_location),
			Some((1, AssetStatus::FrozenXcmDepositAllowed))
		);
		// Unfreeze foreign asset
1
		assert_ok!(EvmForeignAssets::unfreeze_foreign_asset(
1
			pallet_xcm::Origin::Xcm(para_location.clone()).into(),
			1,
		));
1
		assert_eq!(
1
			EvmForeignAssets::assets_by_location(&asset_location),
			Some((1, AssetStatus::Active))
		);
1
	});
1
}
// The precoompile asset-erc20 is deprecated and not used anymore for new evm foreign assets
// We don't have testing tools in rust test to call real evm smart contract, so we rely on ts tests.
/*
#[test]
fn xcm_asset_erc20_precompiles_supply_and_balance() {
	ExtBuilder::default()
		.with_xcm_assets(vec![XcmAssetInitialization {
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
			metadata: AssetRegistrarMetadata {
				name: b"RelayToken".to_vec(),
				symbol: b"Relay".to_vec(),
				decimals: 12,
				is_frozen: false,
			},
			balances: vec![(AccountId::from(ALICE), 1_000 * MOVR)],
			is_sufficient: true,
		}])
		.with_balances(vec![
			(AccountId::from(ALICE), 2_000 * MOVR),
			(AccountId::from(BOB), 1_000 * MOVR),
		])
		.build()
		.execute_with(|| {
			// We have the assetId that corresponds to the relay chain registered
			let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into();
			// Its address is
			let asset_precompile_address = Runtime::asset_id_to_account(
				FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
				relay_asset_id,
			);
			// Assert the asset has been created with the correct supply
			assert_eq!(
				moonriver_runtime::Assets::total_supply(relay_asset_id),
				1_000 * MOVR
			);
			// Access totalSupply through precompile. Important that the context is correct
			Precompiles::new()
				.prepare_test(
					ALICE,
					asset_precompile_address,
					ForeignAssetsPCall::total_supply {},
				)
				.expect_cost(5007)
				.expect_no_logs()
				.execute_returns(U256::from(1000 * MOVR));
			// Access balanceOf through precompile
			Precompiles::new()
				.prepare_test(
					ALICE,
					asset_precompile_address,
					ForeignAssetsPCall::balance_of {
						who: Address(ALICE.into()),
					},
				)
				.expect_cost(5007)
				.expect_no_logs()
				.execute_returns(U256::from(1000 * MOVR));
		});
}
#[test]
fn xcm_asset_erc20_precompiles_transfer() {
	ExtBuilder::default()
		.with_xcm_assets(vec![XcmAssetInitialization {
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
			metadata: AssetRegistrarMetadata {
				name: b"RelayToken".to_vec(),
				symbol: b"Relay".to_vec(),
				decimals: 12,
				is_frozen: false,
			},
			balances: vec![(AccountId::from(ALICE), 1_000 * MOVR)],
			is_sufficient: true,
		}])
		.with_balances(vec![
			(AccountId::from(ALICE), 2_000 * MOVR),
			(AccountId::from(BOB), 1_000 * MOVR),
		])
		.build()
		.execute_with(|| {
			// We have the assetId that corresponds to the relay chain registered
			let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into();
			// Its address is
			let asset_precompile_address = Runtime::asset_id_to_account(
				FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
				relay_asset_id,
			);
			// Transfer tokens from Aice to Bob, 400 MOVR.
			Precompiles::new()
				.prepare_test(
					ALICE,
					asset_precompile_address,
					ForeignAssetsPCall::transfer {
						to: Address(BOB.into()),
						value: { 400 * MOVR }.into(),
					},
				)
				.expect_cost(26587)
				.expect_log(log3(
					asset_precompile_address,
					SELECTOR_LOG_TRANSFER,
					H160::from(ALICE),
					H160::from(BOB),
					solidity::encode_event_data(U256::from(400 * MOVR)),
				))
				.execute_returns(true);
			// Make sure BOB has 400 MOVR
			Precompiles::new()
				.prepare_test(
					BOB,
					asset_precompile_address,
					ForeignAssetsPCall::balance_of {
						who: Address(BOB.into()),
					},
				)
				.expect_cost(5007)
				.expect_no_logs()
				.execute_returns(U256::from(400 * MOVR));
		});
}
#[test]
fn xcm_asset_erc20_precompiles_approve() {
	ExtBuilder::default()
		.with_xcm_assets(vec![XcmAssetInitialization {
			asset_type: AssetType::Xcm(xcm::v3::Location::parent()),
			metadata: AssetRegistrarMetadata {
				name: b"RelayToken".to_vec(),
				symbol: b"Relay".to_vec(),
				decimals: 12,
				is_frozen: false,
			},
			balances: vec![(AccountId::from(ALICE), 1_000 * MOVR)],
			is_sufficient: true,
		}])
		.with_balances(vec![
			(AccountId::from(ALICE), 2_000 * MOVR),
			(AccountId::from(BOB), 1_000 * MOVR),
		])
		.build()
		.execute_with(|| {
			// We have the assetId that corresponds to the relay chain registered
			let relay_asset_id: AssetId = AssetType::Xcm(xcm::v3::Location::parent()).into();
			// Its address is
			let asset_precompile_address = Runtime::asset_id_to_account(
				FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
				relay_asset_id,
			);
			// Aprove Bob for spending 400 MOVR from Alice
			Precompiles::new()
				.prepare_test(
					ALICE,
					asset_precompile_address,
					ForeignAssetsPCall::approve {
						spender: Address(BOB.into()),
						value: { 400 * MOVR }.into(),
					},
				)
				.expect_cost(17323)
				.expect_log(log3(
					asset_precompile_address,
					SELECTOR_LOG_APPROVAL,
					H160::from(ALICE),
					H160::from(BOB),
					solidity::encode_event_data(U256::from(400 * MOVR)),
				))
				.execute_returns(true);
			// Transfer tokens from Alice to Charlie by using BOB as origin
			Precompiles::new()
				.prepare_test(
					BOB,
					asset_precompile_address,
					ForeignAssetsPCall::transfer_from {
						from: Address(ALICE.into()),
						to: Address(CHARLIE.into()),
						value: { 400 * MOVR }.into(),
					},
				)
				.expect_cost(31873)
				.expect_log(log3(
					asset_precompile_address,
					SELECTOR_LOG_TRANSFER,
					H160::from(ALICE),
					H160::from(CHARLIE),
					solidity::encode_event_data(U256::from(400 * MOVR)),
				))
				.execute_returns(true);
			// Make sure CHARLIE has 400 MOVR
			Precompiles::new()
				.prepare_test(
					CHARLIE,
					asset_precompile_address,
					ForeignAssetsPCall::balance_of {
						who: Address(CHARLIE.into()),
					},
				)
				.expect_cost(5007)
				.expect_no_logs()
				.execute_returns(U256::from(400 * MOVR));
		});
}*/
#[test]
1
fn make_sure_polkadot_xcm_cannot_be_called() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * MOVR),
1
			(AccountId::from(BOB), 1_000 * MOVR),
1
		])
1
		.with_collators(vec![(AccountId::from(ALICE), 1_000 * MOVR)])
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(moonriver_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::from(dest.clone())),
1
					beneficiary: Box::new(VersionedLocation::from(dest)),
1
					assets: Box::new(VersionedAssets::from(assets)),
1
					fee_asset_item: 0,
1
				})
1
				.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::signed(
1
					AccountId::from(ALICE)
				)),
1
				frame_system::Error::<Runtime>::CallFiltered
			);
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 * MOVR),
1
			(AccountId::from(BOB), 1_000 * MOVR),
1
		])
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_id: 1,
1
			xcm_location: xcm::v5::Location::parent(),
1
			name: "RelayToken",
1
			symbol: "Relay",
1
			decimals: 12,
1
			balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)],
1
		}])
1
		.build()
1
		.execute_with(|| {
1
			let source_id: moonriver_runtime::AssetId = 1;
1
			assert_ok!(XcmTransactor::register(
1
				root_origin(),
1
				AccountId::from(ALICE),
				0,
			));
			// Root can set transact info
1
			assert_ok!(XcmTransactor::set_transact_info(
1
				root_origin(),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
				// Relay charges 1000 for every instruction, and we have 3, so 3000
1
				3000.into(),
1
				20000.into(),
1
				None
			));
			// Set fee per second using weight-trader (replaces old set_fee_per_second)
1
			set_fee_per_second_for_location(Location::parent(), 1).expect("must succeed");
1
			assert_noop!(
1
				XcmTransactor::transact_through_derivative(
1
					origin_of(AccountId::from(ALICE)),
1
					moonriver_runtime::xcm_config::Transactors::Relay,
					0,
1
					CurrencyPayment {
1
						currency: Currency::AsMultiLocation(Box::new(
1
							xcm::VersionedLocation::from(Location::parent())
1
						)),
1
						fee_amount: None
1
					},
1
					vec![],
					// 20000 is the max
1
					TransactWeights {
1
						transact_required_weight_at_most: 17001.into(),
1
						overall_weight: None
1
					},
					false
				),
1
				pallet_xcm_transactor::Error::<Runtime>::MaxWeightTransactReached
			);
1
			assert_noop!(
1
				XcmTransactor::transact_through_derivative(
1
					origin_of(AccountId::from(ALICE)),
1
					moonriver_runtime::xcm_config::Transactors::Relay,
					0,
1
					CurrencyPayment {
1
						currency: Currency::AsCurrencyId(
1
							moonriver_runtime::xcm_config::CurrencyId::ForeignAsset(source_id)
1
						),
1
						fee_amount: None
1
					},
1
					vec![],
					// 20000 is the max
1
					TransactWeights {
1
						transact_required_weight_at_most: 17001.into(),
1
						overall_weight: None
1
					},
					false
				),
1
				pallet_xcm_transactor::Error::<Runtime>::MaxWeightTransactReached
			);
1
		})
1
}
#[test]
1
fn transact_through_signed_precompile_works_v2() {
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * MOVR),
1
			(AccountId::from(BOB), 1_000 * MOVR),
1
		])
1
		.with_safe_xcm_version(3)
1
		.build()
1
		.execute_with(|| {
			// Destination
1
			let dest = Location::parent();
1
			let fee_payer_asset = Location::parent();
1
			let bytes = vec![1u8, 2u8, 3u8];
1
			let total_weight = 1_000_000_000u64;
1
			let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061);
1
			Precompiles::new()
1
				.prepare_test(
					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
				.expect_cost(31240)
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 * MOVR),
1
			(AccountId::from(BOB), 1_000 * MOVR),
1
		])
1
		.with_safe_xcm_version(3)
1
		.build()
1
		.execute_with(|| {
			// Destination
1
			let dest = Location::here();
1
			let fee_payer_asset = Location::parent();
1
			let bytes = vec![1u8, 2u8, 3u8];
1
			let total_weight = 1_000_000_000u64;
1
			let xcm_transactor_v2_precompile_address = H160::from_low_u64_be(2061);
1
			Precompiles::new()
1
				.prepare_test(
					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
				.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 call_pallet_xcm_with_fee() {
1
	let asset_id = 1;
1
	ExtBuilder::default()
1
		.with_balances(vec![
1
			(AccountId::from(ALICE), 2_000 * MOVR),
1
			(AccountId::from(BOB), 1_000 * MOVR),
1
		])
1
		.with_safe_xcm_version(3)
1
		.with_xcm_assets(vec![XcmAssetInitialization {
1
			asset_id,
1
			xcm_location: Location::parent(),
1
			name: "RelayToken",
1
			symbol: "Relay",
1
			decimals: 12,
1
			balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)],
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 before_balance =
1
				EvmForeignAssets::balance(asset_id, AccountId::from(ALICE)).unwrap();
1
			let (chain_part, beneficiary) =
1
				split_location_into_chain_part_and_beneficiary(dest).unwrap();
1
			let asset = currency_to_asset(CurrencyId::ForeignAsset(asset_id), 100_000_000_000_000);
1
			let asset_fee = currency_to_asset(CurrencyId::ForeignAsset(asset_id), 100);
1
			let fees_id: VersionedAssetId = AssetId(Location::parent()).into();
1
			let xcm_on_dest = Xcm::<()>(vec![DepositAsset {
1
				assets: Wild(All),
1
				beneficiary: beneficiary.clone(),
1
			}]);
1
			assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then(
1
				origin_of(AccountId::from(ALICE)),
1
				Box::new(VersionedLocation::from(chain_part)),
1
				Box::new(VersionedAssets::from(vec![asset_fee, asset])),
1
				Box::new(TransferType::DestinationReserve),
1
				Box::new(fees_id),
1
				Box::new(TransferType::DestinationReserve),
1
				Box::new(VersionedXcm::V5(xcm_on_dest)),
1
				WeightLimit::Limited(4000000000.into())
			));
1
			let after_balance =
1
				EvmForeignAssets::balance(asset_id, AccountId::from(ALICE)).unwrap();
			// Balance should have been reduced by the transfer amount plus fees
1
			assert!(after_balance < before_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
		Precompiles::new()
1
			.prepare_test(
				ALICE,
1
				xcm_utils_precompile_address,
1
				XcmUtilsPCall::multilocation_to_address {
1
					location: Location::parent(),
1
				},
			)
1
			.expect_cost(
1
				<Runtime as frame_system::Config>::DbWeight::get()
1
					.read
1
					.saturating_div(WEIGHT_PER_GAS)
1
					.saturating_mul(2),
			)
1
			.expect_no_logs()
1
			.execute_returns(Address(expected_address_parent));
1
		let parachain_2000_multilocation = Location::new(1, [Parachain(2000)]);
1
		let expected_address_parachain: H160 =
1
			SiblingParachainConvertsVia::<Sibling, AccountId>::convert_location(
1
				&parachain_2000_multilocation,
			)
1
			.unwrap()
1
			.into();
1
		Precompiles::new()
1
			.prepare_test(
				ALICE,
1
				xcm_utils_precompile_address,
1
				XcmUtilsPCall::multilocation_to_address {
1
					location: parachain_2000_multilocation,
1
				},
			)
1
			.expect_cost(
1
				<Runtime as frame_system::Config>::DbWeight::get()
1
					.read
1
					.saturating_div(WEIGHT_PER_GAS)
1
					.saturating_mul(2),
			)
1
			.expect_no_logs()
1
			.execute_returns(Address(expected_address_parachain));
1
		let alice_in_parachain_2000_location = Location::new(
			1,
1
			[
1
				Parachain(2000),
1
				AccountKey20 {
1
					network: None,
1
					key: ALICE,
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
		Precompiles::new()
1
			.prepare_test(
				ALICE,
1
				xcm_utils_precompile_address,
1
				XcmUtilsPCall::multilocation_to_address {
1
					location: alice_in_parachain_2000_location,
1
				},
			)
1
			.expect_cost(
1
				<Runtime as frame_system::Config>::DbWeight::get()
1
					.read
1
					.saturating_div(WEIGHT_PER_GAS)
1
					.saturating_mul(2),
			)
1
			.expect_no_logs()
1
			.execute_returns(Address(expected_address_alice_in_parachain_2000));
1
	});
1
}
#[test]
1
fn test_nested_batch_calls_from_xcm_transact() {
1
	ExtBuilder::default().build().execute_with(|| {
		// This ensures we notice if MAX_XCM_DECODE_DEPTH changes
		// in a future polkadot-sdk version
1
		assert_eq!(xcm::MAX_XCM_DECODE_DEPTH, 8);
1
		let mut valid_nested_calls =
1
			RuntimeCall::System(frame_system::Call::remark { remark: vec![] });
9
		for _ in 0..xcm::MAX_XCM_DECODE_DEPTH {
8
			valid_nested_calls = RuntimeCall::Utility(pallet_utility::Call::batch {
8
				calls: vec![valid_nested_calls],
8
			});
8
		}
1
		let valid_message = Xcm(vec![Transact {
1
			origin_kind: OriginKind::SovereignAccount,
1
			fallback_max_weight: None,
1
			call: valid_nested_calls.encode().into(),
1
		}]);
1
		assert!(XcmExecutor::prepare(valid_message, Weight::MAX).is_ok());
1
		let excessive_nested_calls = RuntimeCall::Utility(pallet_utility::Call::batch {
1
			calls: vec![valid_nested_calls],
1
		});
1
		let invalid_message = Xcm(vec![Transact {
1
			origin_kind: OriginKind::SovereignAccount,
1
			fallback_max_weight: None,
1
			call: excessive_nested_calls.encode().into(),
1
		}]);
		// Expect to fail because we have too many nested calls
1
		assert!(XcmExecutor::prepare(invalid_message, Weight::MAX).is_err());
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::<moonriver_runtime::Runtime, RuntimeCall>::clear_origin().ref_time();
1
		let message: Vec<u8> = xcm::VersionedXcm::<()>::V5(Xcm(vec![ClearOrigin])).encode();
1
		let input = XcmUtilsPCall::weight_message {
1
			message: message.into(),
1
		};
1
		Precompiles::new()
1
			.prepare_test(ALICE, xcm_utils_precompile_address, input)
1
			.expect_cost(
1
				<Runtime as frame_system::Config>::DbWeight::get()
1
					.read
1
					.saturating_div(WEIGHT_PER_GAS),
			)
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
		let input = XcmUtilsPCall::get_units_per_second { location };
1
		let expected_units =
1
			WEIGHT_REF_TIME_PER_SECOND as u128 * moonriver_runtime::currency::WEIGHT_FEE;
1
		Precompiles::new()
1
			.prepare_test(ALICE, xcm_utils_precompile_address, input)
1
			.expect_cost(
1
				<Runtime as frame_system::Config>::DbWeight::get()
1
					.read
1
					.saturating_div(WEIGHT_PER_GAS)
1
					.saturating_mul(2),
			)
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, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 256, 1024, 1025, 1026, 2048,
			2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062,
			2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074,
		]
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
			if precompile_addresses.contains(&address) {
47
				assert!(
47
					is_precompile_or_fail::<Runtime>(address, 100_000u64).expect("to be ok"),
					"is_precompile({}) should return true",
					i
				);
47
				assert!(
47
					precompiles
47
						.execute(&mut MockHandle::new(
47
							address,
47
							Context {
47
								address,
47
								caller: H160::zero(),
47
								apparent_value: U256::zero()
47
							}
47
						),)
47
						.is_some(),
					"execute({},..) should return Some(_)",
					i
				);
			} else {
2953
				assert!(
2953
					!is_precompile_or_fail::<Runtime>(address, 100_000u64).expect("to be ok"),
					"is_precompile({}) should return false",
					i
				);
2953
				assert!(
2953
					precompiles
2953
						.execute(&mut MockHandle::new(
2953
							address,
2953
							Context {
2953
								address,
2953
								caller: H160::zero(),
2953
								apparent_value: U256::zero()
2953
							}
2953
						),)
2953
						.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, 1027, 2051, 2062, 2063];
3000
		for i in 1..3000 {
2999
			let address = H160::from_low_u64_be(i);
2999
			if !is_precompile_or_fail::<Runtime>(address, 100_000u64).expect("to be ok") {
2952
				continue;
47
			}
47
			if !removed_precompiles.contains(&i) {
43
				assert!(
43
					match precompiles.is_active_precompile(address, 100_000u64) {
43
						IsPrecompileResult::Answer { is_precompile, .. } => is_precompile,
						_ => false,
					},
					"{i} should be an active precompile"
				);
43
				continue;
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() {
	use frame_support::traits::OnUnbalanced;
	use moonbeam_runtime_common::deal_with_fees::DealWithSubstrateFeesAndTip;
	use moonriver_runtime::Treasury;
1
	ExtBuilder::default().build().execute_with(|| {
1
		set_parachain_inherent_data();
		// This test validates the functionality of the `DealWithSubstrateFeesAndTip` trait implementation
		// in the Moonriver runtime. It verifies that:
		// - The correct proportion of the fee is sent to the treasury.
		// - The remaining fee is burned (removed from the total supply).
		// - The entire tip is sent to the block author.
		// The test details:
		// 1. Simulate issuing a `fee` of 100 and a `tip` of 1000.
		// 2. Confirm the initial total supply is 1,100 (equal to the sum of the issued fee and tip).
		// 3. Confirm the treasury's balance is initially 0.
		// 4. Execute the `DealWithSubstrateFeesAndTip::on_unbalanceds` function with the `fee` and `tip`.
		// 5. Validate that the treasury's balance has increased by 20% of the fee (based on FeesTreasuryProportion).
		// 6. Validate that 80% of the fee is burned, and the total supply decreases accordingly.
		// 7. Validate that the entire tip (100%) is sent to the block author (collator).
		// Step 1: Issue the fee and tip amounts.
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);
		// Step 2: Validate the initial supply and balances.
1
		let total_supply_before = Balances::total_issuance();
1
		let block_author = pallet_author_inherent::Pallet::<Runtime>::get();
1
		let block_author_balance_before = Balances::free_balance(&block_author);
1
		assert_eq!(total_supply_before, 1_100);
1
		assert_eq!(Balances::free_balance(&Treasury::account_id()), 0);
		// Step 3: Execute the fees handling logic.
1
		DealWithSubstrateFeesAndTip::<
1
			Runtime,
1
			dynamic_params::runtime_config::FeesTreasuryProportion,
1
		>::on_unbalanceds(vec![fee, tip].into_iter());
		// Step 4: Compute the split between treasury and burned fees based on FeesTreasuryProportion (20%).
1
		let treasury_proportion = dynamic_params::runtime_config::FeesTreasuryProportion::get();
1
		let treasury_fee_part: Balance = treasury_proportion.mul_floor(100);
1
		let burnt_fee_part: Balance = 100 - treasury_fee_part;
		// Step 5: Validate the treasury received 20% of the fee.
1
		assert_eq!(
1
			Balances::free_balance(&Treasury::account_id()),
			treasury_fee_part,
		);
		// Step 6: Verify that 80% of the fee was burned (removed from the total supply).
1
		let total_supply_after = Balances::total_issuance();
1
		assert_eq!(total_supply_before - total_supply_after, burnt_fee_part,);
		// Step 7: Validate that the block author (collator) received 100% of the tip.
1
		let block_author_balance_after = Balances::free_balance(&block_author);
1
		assert_eq!(
1
			block_author_balance_after - block_author_balance_before,
			1000,
		);
1
	});
1
}
#[test]
1
fn evm_revert_substrate_events() {
1
	ExtBuilder::default()
1
		.with_balances(vec![(AccountId::from(ALICE), 1_000 * MOVR)])
1
		.build()
1
		.execute_with(|| {
1
			let batch_precompile_address = H160::from_low_u64_be(2056);
			// Batch a transfer followed by an invalid call to batch.
			// 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
				input: BatchPCall::batch_all {
1
					to: vec![Address(BOB.into()), Address(batch_precompile_address)].into(),
1
					value: vec![U256::from(1 * MOVR), 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: U256::from(BASE_FEE_GENESIS),
1
				max_priority_fee_per_gas: None,
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
				authorization_list: Vec::new(),
1
			})
1
			.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
1
			let transfer_count = System::events()
1
				.iter()
4
				.filter(|r| match r.event {
					RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true,
6
					_ => false,
6
				})
1
				.count();
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), 1_000 * MOVR)])
1
		.build()
1
		.execute_with(|| {
1
			let batch_precompile_address = H160::from_low_u64_be(2056);
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 * MOVR)].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: U256::from(BASE_FEE_GENESIS),
1
				max_priority_fee_per_gas: None,
1
				nonce: Some(U256::from(0)),
1
				access_list: Vec::new(),
1
				authorization_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 {
1
					RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => true,
9
					_ => false,
10
				})
1
				.count();
1
			assert_eq!(transfer_count, 1, "there should be 1 transfer event");
1
		});
1
}
#[cfg(test)]
mod bridge_tests {
	use crate::common::{origin_of, root_origin, ExtBuilder, XcmAssetInitialization, ALICE, BOB};
	use crate::currency_to_asset;
	use bp_messages::target_chain::DispatchMessageData;
	use bp_messages::ReceptionResult;
	use bp_runtime::messages::MessageDispatchResult;
	use cumulus_primitives_core::AggregateMessageOrigin;
	use frame_support::assert_ok;
	use frame_support::pallet_prelude::{Hooks, PalletInfoAccess};
	use moonbeam_core_primitives::AccountId;
	use moonriver_runtime::bridge_config::{
		PolkadotGlobalConsensusNetwork, WithPolkadotMessagesInstance,
	};
	use moonriver_runtime::currency::MOVR;
	use moonriver_runtime::xcm_config::CurrencyId;
	use moonriver_runtime::{
		Balances, BridgePolkadotMessages, BridgeXcmOverMoonbeam, MessageQueue, PolkadotXcm,
		Runtime, RuntimeEvent, System,
	};
	use pallet_bridge_messages::LanesManager;
	use pallet_xcm_bridge::XcmBlobMessageDispatchResult::Dispatched;
	use parity_scale_codec::Encode;
	use sp_core::H256;
	use xcm::latest::Junctions::X1;
	use xcm::latest::{
		Asset, AssetFilter, AssetId, Junctions, Location, NetworkId, WeightLimit, WildAsset, Xcm,
	};
	use xcm::prelude::{
		AccountKey20, BuyExecution, ClearOrigin, DepositAsset, DescendOrigin, Fungible,
		GlobalConsensus, PalletInstance, Parachain, ReserveAssetDeposited, UniversalOrigin,
		XCM_VERSION,
	};
	use xcm::{VersionedAssets, VersionedInteriorLocation, VersionedLocation, VersionedXcm};
	use xcm_builder::BridgeMessage;
1
	fn next_block() {
1
		System::reset_events();
1
		let next_block = System::block_number() + 1u32;
1
		System::set_block_number(next_block);
1
		System::on_initialize(next_block);
1
		MessageQueue::on_initialize(next_block);
1
	}
	#[test]
1
	fn transfer_asset_moonriver_to_moonbeam() {
1
		frame_support::__private::sp_tracing::init_for_tests();
1
		ExtBuilder::default()
1
			.with_balances(vec![
1
				(AccountId::from(ALICE), 2_000 * MOVR),
1
				(AccountId::from(BOB), 1_000 * MOVR),
1
			])
1
			.with_safe_xcm_version(XCM_VERSION)
1
			.with_open_bridges(vec![(
1
				Location::new(
1
					1,
1
					[Parachain(
1
						<bp_moonriver::Moonriver as bp_runtime::Parachain>::PARACHAIN_ID,
1
					)],
1
				),
1
				Junctions::from([
1
					NetworkId::Polkadot.into(),
1
					Parachain(<bp_moonbeam::Moonbeam as bp_runtime::Parachain>::PARACHAIN_ID),
1
				]),
1
				Some(bp_moonriver::LaneId::from_inner(H256([0u8; 32]))),
1
			)])
1
			.build()
1
			.execute_with(|| {
1
				assert_ok!(PolkadotXcm::force_xcm_version(
1
					root_origin(),
1
					Box::new(bp_moonbeam::GlobalConsensusLocation::get()),
					XCM_VERSION
				));
1
				let asset = currency_to_asset(CurrencyId::SelfReserve, 100 * MOVR);
1
				let message_data = BridgePolkadotMessages::outbound_message_data(
1
					bp_moonriver::LaneId::from_inner(H256([0u8; 32])),
					1u64,
				);
1
				assert!(message_data.is_none());
1
				assert_ok!(PolkadotXcm::transfer_assets(
1
					origin_of(AccountId::from(ALICE)),
1
					Box::new(VersionedLocation::V5(
1
						bp_moonbeam::GlobalConsensusLocation::get()
1
					)),
1
					Box::new(VersionedLocation::V5(Location {
1
						parents: 0,
1
						interior: [AccountKey20 {
1
							network: None,
1
							key: ALICE,
1
						}]
1
						.into(),
1
					})),
1
					Box::new(VersionedAssets::V5(asset.into())),
					0,
1
					WeightLimit::Unlimited
				));
1
				let message_data = BridgePolkadotMessages::outbound_message_data(
1
					bp_moonriver::LaneId::from_inner(H256([0u8; 32])),
					1u64,
				);
1
				assert!(message_data.is_some());
1
			})
1
	}
	#[test]
1
	fn receive_message_from_moonbeam() {
1
		frame_support::__private::sp_tracing::init_for_tests();
1
		ExtBuilder::default()
1
			.with_balances(vec![
1
				(AccountId::from(ALICE), 2_000 * MOVR),
1
				(AccountId::from(BOB), 1_000 * MOVR),
1
			])
1
			.with_xcm_assets(vec![XcmAssetInitialization {
1
				asset_id: 1,
1
				xcm_location: Location::new(
1
					2,
1
					[
1
						GlobalConsensus(PolkadotGlobalConsensusNetwork::get()),
1
						Parachain(<bp_moonbeam::Moonbeam as bp_runtime::Parachain>::PARACHAIN_ID),
1
						PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
1
					]
1
				),
1
				name: "xcGLMR",
1
				symbol: "xcGLMR",
1
				decimals: 18,
1
				balances: vec![(AccountId::from(ALICE), 1_000_000_000_000_000)],
1
			}])
1
			.with_safe_xcm_version(XCM_VERSION)
1
			.with_open_bridges(vec![(
1
				Location::new(
1
					1,
1
					[Parachain(
1
						<bp_moonriver::Moonriver as bp_runtime::Parachain>::PARACHAIN_ID,
1
					)],
1
				),
1
				Junctions::from([
1
					NetworkId::Polkadot.into(),
1
					Parachain(<bp_moonbeam::Moonbeam as bp_runtime::Parachain>::PARACHAIN_ID),
1
				]),
1
				Some(bp_moonriver::LaneId::from_inner(H256([0u8; 32])))
1
			)])
1
			.build()
1
			.execute_with(|| {
1
				let bridge_message: BridgeMessage = BridgeMessage {
1
					universal_dest: VersionedInteriorLocation::V5(
1
						[
1
							GlobalConsensus(NetworkId::Kusama),
1
							Parachain(<bp_moonriver::Moonriver as bp_runtime::Parachain>::PARACHAIN_ID)
1
						].into()
1
					),
1
					message: VersionedXcm::V5(
1
						Xcm(
1
							[
1
								UniversalOrigin(GlobalConsensus(NetworkId::Polkadot)),
1
								DescendOrigin(X1([Parachain(<bp_moonbeam::Moonbeam as bp_runtime::Parachain>::PARACHAIN_ID)].into())),
1
								ReserveAssetDeposited(
1
									vec![
1
										Asset {
1
											id: AssetId(
1
												Location::new(
1
													2,
1
													[
1
														GlobalConsensus(NetworkId::Polkadot),
1
														Parachain(<bp_moonbeam::Moonbeam as bp_runtime::Parachain>::PARACHAIN_ID),
1
														PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
1
													]
1
												)
1
											),
1
											fun: Fungible(2 * MOVR)
1
										}
1
									].into()
1
								),
1
								ClearOrigin,
1
								BuyExecution {
1
									fees: Asset {
1
										id: AssetId(
1
											Location::new(
1
												2,
1
												[
1
													GlobalConsensus(NetworkId::Polkadot),
1
													Parachain(<bp_moonbeam::Moonbeam as bp_runtime::Parachain>::PARACHAIN_ID),
1
													PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
1
												]
1
											)
1
										),
1
										fun:  Fungible(MOVR / 2)
1
									},
1
									weight_limit: WeightLimit::Unlimited
1
								},
1
								DepositAsset {
1
									assets: AssetFilter::Wild(WildAsset::AllCounted(1)),
1
									beneficiary: Location::new(0, [AccountKey20 { network: None, key: ALICE }]),
1
								}
1
							].into()
1
						)
1
					)
1
				};
1
				let mut inbound_lane = LanesManager::<Runtime, WithPolkadotMessagesInstance>::new()
1
					.active_inbound_lane(Default::default())
1
					.unwrap();
1
				let msg = DispatchMessageData { payload: Ok(bridge_message.encode()) };
1
				let result = inbound_lane.receive_message::<BridgeXcmOverMoonbeam>(
1
					&AccountId::from(ALICE),
					1,
1
					msg,
				);
1
				assert_eq!(result, ReceptionResult::Dispatched(MessageDispatchResult { unspent_weight: Default::default(), dispatch_level_result: Dispatched }));
				// Produce next block
1
				next_block();
				// Confirm that the xcm message was successfully processed
7
				assert!(System::events().iter().any(|evt| {
1
					matches!(
1
						evt.event,
						RuntimeEvent::MessageQueue(
							pallet_message_queue::Event::Processed {
								origin: AggregateMessageOrigin::Here,
								success: true,
								..
							}
						)
					)
7
				}));
1
			});
1
	}
}
#[cfg(test)]
mod treasury_tests {
	use super::*;
	use frame_support::traits::fungible::NativeOrWithId;
	use moonriver_runtime::XcmWeightTrader;
	use sp_core::bounded_vec;
	use sp_runtime::traits::Hash;
4
	fn expect_events(events: Vec<RuntimeEvent>) {
4
		let block_events: Vec<RuntimeEvent> =
4
			System::events().into_iter().map(|r| r.event).collect();
7
		assert!(events.iter().all(|evt| block_events.contains(evt)))
4
	}
7
	fn next_block() {
7
		System::reset_events();
7
		System::set_block_number(System::block_number() + 1u32);
7
		System::on_initialize(System::block_number());
7
		Treasury::on_initialize(System::block_number());
7
	}
3
	fn get_asset_balance(id: &u128, account: &AccountId) -> U256 {
3
		pallet_moonbeam_foreign_assets::Pallet::<Runtime>::balance(id.clone(), account.clone())
3
			.expect("failed to get account balance")
3
	}
	#[test]
1
	fn test_treasury_spend_local_with_council_origin() {
1
		let initial_treasury_balance = 1_000 * MOVR;
1
		ExtBuilder::default()
1
			.with_balances(vec![
1
				(AccountId::from(ALICE), 2_000 * MOVR),
1
				(Treasury::account_id(), initial_treasury_balance),
1
			])
1
			.build()
1
			.execute_with(|| {
1
				let spend_amount = 100u128 * MOVR;
1
				let spend_beneficiary = AccountId::from(BOB);
1
				next_block();
				// TreasuryCouncilCollective
1
				assert_ok!(TreasuryCouncilCollective::set_members(
1
					root_origin(),
1
					vec![AccountId::from(ALICE)],
1
					Some(AccountId::from(ALICE)),
					1
				));
1
				next_block();
				// Perform treasury spending
1
				let valid_from = System::block_number() + 5u32;
1
				let proposal = RuntimeCall::Treasury(pallet_treasury::Call::spend {
1
					amount: spend_amount,
1
					asset_kind: Box::new(NativeOrWithId::Native),
1
					beneficiary: Box::new(AccountId::from(BOB)),
1
					valid_from: Some(valid_from),
1
				});
1
				assert_ok!(TreasuryCouncilCollective::propose(
1
					origin_of(AccountId::from(ALICE)),
					1,
1
					Box::new(proposal.clone()),
					1_000
				));
1
				let payout_period =
1
					<<Runtime as pallet_treasury::Config>::PayoutPeriod as Get<u32>>::get();
1
				let expected_events = [
1
					RuntimeEvent::Treasury(pallet_treasury::Event::AssetSpendApproved {
1
						index: 0,
1
						asset_kind: NativeOrWithId::Native,
1
						amount: spend_amount,
1
						beneficiary: spend_beneficiary,
1
						valid_from,
1
						expire_at: payout_period + valid_from,
1
					}),
1
					RuntimeEvent::TreasuryCouncilCollective(pallet_collective::Event::Executed {
1
						proposal_hash: sp_runtime::traits::BlakeTwo256::hash_of(&proposal),
1
						result: Ok(()),
1
					}),
1
				]
1
				.to_vec();
1
				expect_events(expected_events);
6
				while System::block_number() < valid_from {
5
					next_block();
5
				}
1
				assert_ok!(Treasury::payout(origin_of(spend_beneficiary), 0));
1
				let expected_events = [
1
					RuntimeEvent::Treasury(pallet_treasury::Event::Paid {
1
						index: 0,
1
						payment_id: (),
1
					}),
1
					RuntimeEvent::Balances(pallet_balances::Event::Transfer {
1
						from: Treasury::account_id(),
1
						to: spend_beneficiary,
1
						amount: spend_amount,
1
					}),
1
				]
1
				.to_vec();
1
				expect_events(expected_events);
1
			});
1
	}
	#[test]
1
	fn test_treasury_spend_foreign_asset_with_council_origin() {
1
		let initial_treasury_balance = 1_000 * MOVR;
1
		let asset_id = 1000100010001000u128;
1
		ExtBuilder::default()
1
			.with_balances(vec![(AccountId::from(ALICE), 2_000 * MOVR)])
1
			.build()
1
			.execute_with(|| {
1
				let spend_amount = 100u128 * MOVR;
1
				let spend_beneficiary = AccountId::from(BOB);
1
				let asset_location: Location = Location {
1
					parents: 1,
1
					interior: Junctions::Here,
1
				};
1
				assert_ok!(EvmForeignAssets::create_foreign_asset(
1
					root_origin(),
1
					asset_id,
1
					asset_location.clone(),
					12,
1
					bounded_vec![b'M', b'T'],
1
					bounded_vec![b'M', b'y', b'T', b'o', b'k'],
				));
1
				assert_ok!(XcmWeightTrader::add_asset(
1
					root_origin(),
1
					asset_location,
					1u128
				));
1
				assert_ok!(EvmForeignAssets::mint_into(
1
					asset_id,
1
					Treasury::account_id(),
1
					initial_treasury_balance.into()
				));
1
				assert_eq!(
1
					get_asset_balance(&asset_id, &Treasury::account_id()),
1
					initial_treasury_balance.into(),
					"Treasury balance not updated"
				);
				// TreasuryCouncilCollective
1
				assert_ok!(TreasuryCouncilCollective::set_members(
1
					root_origin(),
1
					vec![AccountId::from(ALICE)],
1
					Some(AccountId::from(ALICE)),
					1
				));
				// Perform treasury spending
1
				let proposal = RuntimeCall::Treasury(pallet_treasury::Call::spend {
1
					amount: spend_amount,
1
					asset_kind: Box::new(NativeOrWithId::WithId(asset_id)),
1
					beneficiary: Box::new(spend_beneficiary),
1
					valid_from: None,
1
				});
1
				assert_ok!(TreasuryCouncilCollective::propose(
1
					origin_of(AccountId::from(ALICE)),
					1,
1
					Box::new(proposal.clone()),
					1_000
				));
1
				let payout_period =
1
					<<Runtime as pallet_treasury::Config>::PayoutPeriod as Get<u32>>::get();
1
				let current_block = System::block_number();
1
				let expected_events = [
1
					RuntimeEvent::Treasury(pallet_treasury::Event::AssetSpendApproved {
1
						index: 0,
1
						asset_kind: NativeOrWithId::WithId(asset_id),
1
						amount: spend_amount,
1
						beneficiary: spend_beneficiary,
1
						valid_from: current_block,
1
						expire_at: current_block + payout_period,
1
					}),
1
					RuntimeEvent::TreasuryCouncilCollective(pallet_collective::Event::Executed {
1
						proposal_hash: sp_runtime::traits::BlakeTwo256::hash_of(&proposal),
1
						result: Ok(()),
1
					}),
1
				]
1
				.to_vec();
1
				expect_events(expected_events);
1
				assert_ok!(Treasury::payout(origin_of(spend_beneficiary), 0));
1
				expect_events(vec![RuntimeEvent::Treasury(pallet_treasury::Event::Paid {
1
					index: 0,
1
					payment_id: (),
1
				})]);
1
				assert_eq!(
1
					get_asset_balance(&asset_id, &Treasury::account_id()),
1
					(initial_treasury_balance - spend_amount).into(),
					"Treasury balance not updated"
				);
1
				assert_eq!(
1
					get_asset_balance(&asset_id, &spend_beneficiary),
1
					spend_amount.into(),
					"Treasury payout failed"
				);
1
			});
1
	}
}
#[cfg(test)]
mod fee_tests {
	use super::*;
	use fp_evm::FeeCalculator;
	use frame_support::{
		traits::{ConstU128, OnFinalize},
		weights::{ConstantMultiplier, WeightToFee},
	};
	use moonriver_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
		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();
		// if the min is too small, then this will not change, and we are doomed forever.
		// 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 = Weight::from_parts(5_000u64, 1);
1
		let tip = 42u128;
		type WeightToFeeImpl = ConstantMultiplier<u128, ConstU128<{ currency::WEIGHT_FEE }>>;
		type LengthToFeeImpl = LengthToFee;
		// base_fee + (multiplier * extrinsic_weight_fee) + extrinsic_length_fee + tip
1
		let expected_fee = WeightToFeeImpl::weight_to_fee(&base_extrinsic)
1
			+ multiplier.saturating_mul_int(WeightToFeeImpl::weight_to_fee(&extrinsic_weight))
1
			+ LengthToFeeImpl::weight_to_fee(&(Weight::from_parts(extrinsic_len as u64, 1)))
1
			+ tip;
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
					call_weight: extrinsic_weight,
1
					extension_weight: Weight::zero(),
1
				},
1
				tip,
			);
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(currency::WEIGHT_FEE.saturating_mul(WEIGHT_PER_GAS as u128))
1
				.into();
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
			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
			assert_ne!(
				a, b,
				"both gas prices were equal, unexpected precision loss incurred"
			);
1
		});
1
	}
	#[test]
1
	fn test_fee_scenarios() {
		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 = (currency::WEIGHT_FEE).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
				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(100_000_000, Perbill::from_percent(0), 1),
1
				U256::from(312_500_000u128), // lower bound enforced
			);
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(25), 1),
1
				U256::from(312_500_000u128),
			);
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(50), 1),
1
				U256::from(312_687_556u128), // slightly higher than lower bound
			);
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(100), 1),
1
				U256::from(313_313_556u128),
			);
			// 1 "real" hour (at 6-second blocks)
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(0), 600),
1
				U256::from(312_500_000u128),
			);
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(25), 600),
1
				U256::from(312_500_000u128),
			);
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(50), 600),
1
				U256::from(447_915_432u128),
			);
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(100), 600),
1
				U256::from(1_487_129_030u128),
			);
			// 1 "real" day (at 6-second blocks)
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(0), 14400),
1
				U256::from(312_500_000u128), // lower bound enforced
			);
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(25), 14400),
1
				U256::from(312_500_000u128),
			);
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(50), 14400),
1
				U256::from(1_766_664_654_709u128),
			);
1
			assert_eq!(
1
				sim(100_000_000, Perbill::from_percent(100), 14400),
1
				U256::from(31_250_000_000_000u128), // upper bound enforced
			);
1
		});
1
	}
}
#[cfg(test)]
mod balance_tests {
	use crate::common::{ExtBuilder, ALICE};
	use frame_support::assert_ok;
	use frame_support::traits::LockableCurrency;
	use frame_support::traits::{LockIdentifier, ReservableCurrency, WithdrawReasons};
	use moonbeam_core_primitives::AccountId;
	use moonriver_runtime::{Balances, Runtime, System};
	#[test]
1
	fn reserve_should_work_for_frozen_balance() {
1
		let alice = AccountId::from(ALICE);
		const ID_1: LockIdentifier = *b"1       ";
1
		ExtBuilder::default()
1
			.with_balances(vec![(alice, 10)])
1
			.build()
1
			.execute_with(|| {
				// Check balances
1
				let account = System::account(&alice).data;
1
				assert_eq!(account.free, 10);
1
				assert_eq!(account.frozen, 0);
1
				assert_eq!(account.reserved, 0);
1
				Balances::set_lock(ID_1, &alice, 9, WithdrawReasons::RESERVE);
1
				let account = System::account(&alice).data;
1
				assert_eq!(account.free, 10);
1
				assert_eq!(account.frozen, 9);
1
				assert_eq!(account.reserved, 0);
1
				assert_ok!(Balances::reserve(&alice, 5));
1
				let account = System::account(&alice).data;
1
				assert_eq!(account.free, 5);
1
				assert_eq!(account.frozen, 9);
1
				assert_eq!(account.reserved, 5);
1
				let previous_reserved_amount = account.reserved;
1
				let ed: u128 = <Runtime as pallet_balances::Config>::ExistentialDeposit::get();
1
				let next_reserve = account.free.saturating_sub(ed);
1
				assert_ok!(Balances::reserve(&alice, next_reserve));
1
				let account = System::account(&alice).data;
1
				assert_eq!(account.free, ed);
1
				assert_eq!(account.frozen, 9);
1
				assert_eq!(
					account.reserved,
1
					previous_reserved_amount.saturating_add(next_reserve)
				);
1
			});
1
	}
}
moonbeam_runtime_common::generate_common_xcm_tests!(moonriver_runtime);