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
//! Test utilities
18
use super::*;
19
use cumulus_primitives_core::{relay_chain::HrmpChannelId, ParaId};
20
use frame_support::traits::{
21
	ConstU32, Disabled, EnsureOrigin, Everything, Nothing, OriginTrait,
22
	PalletInfo as PalletInfoTrait,
23
};
24
use frame_support::{construct_runtime, parameter_types, weights::Weight};
25
use moonbeam_tests_primitives::MemoryFeeTrader;
26
use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, FrameSystemAccountProvider};
27
use pallet_xcm_transactor::RelayIndices;
28
use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode};
29
use precompile_utils::{
30
	mock_account,
31
	precompile_set::*,
32
	testing::{AddressInPrefixedSet, MockAccount},
33
};
34
use scale_info::TypeInfo;
35
use sp_core::H256;
36
use sp_io;
37
use sp_runtime::traits::{BlakeTwo256, IdentityLookup};
38
use sp_runtime::BuildStorage;
39
use xcm::latest::{prelude::*, Error as XcmError};
40
use xcm_builder::{AllowUnpaidExecutionFrom, FixedWeightBounds, IsConcrete};
41
use xcm_executor::{
42
	traits::{TransactAsset, WeightTrader},
43
	AssetsInHolding,
44
};
45

            
46
pub type AccountId = MockAccount;
47
pub type Balance = u128;
48
pub type AssetId = u128;
49

            
50
type Block = frame_system::mocking::MockBlockU32<Runtime>;
51

            
52
// Configure a mock runtime to test the pallet.
53
construct_runtime!(
54
	pub enum Runtime {
55
		System: frame_system,
56
		Balances: pallet_balances,
57
		Evm: pallet_evm,
58
		Timestamp: pallet_timestamp,
59
		PolkadotXcm: pallet_xcm,
60
		XcmTransactor: pallet_xcm_transactor,
61
	}
62
);
63

            
64
28
mock_account!(AssetAccount(u128), |v: AssetAccount| AddressInPrefixedSet(
65
28
	0xffffffff, v.0
66
28
)
67
28
.into());
68
2
mock_account!(SelfReserveAccount, |_| MockAccount::from_u64(2));
69

            
70
parameter_types! {
71
	pub ParachainId: cumulus_primitives_core::ParaId = 100.into();
72
}
73

            
74
parameter_types! {
75
	pub const BlockHashCount: u32 = 250;
76
	pub const SS58Prefix: u8 = 42;
77
}
78
impl frame_system::Config for Runtime {
79
	type BaseCallFilter = Everything;
80
	type DbWeight = ();
81
	type RuntimeOrigin = RuntimeOrigin;
82
	type RuntimeTask = RuntimeTask;
83
	type Nonce = u64;
84
	type Block = Block;
85
	type RuntimeCall = RuntimeCall;
86
	type Hash = H256;
87
	type Hashing = BlakeTwo256;
88
	type AccountId = AccountId;
89
	type Lookup = IdentityLookup<Self::AccountId>;
90
	type RuntimeEvent = RuntimeEvent;
91
	type BlockHashCount = BlockHashCount;
92
	type Version = ();
93
	type PalletInfo = PalletInfo;
94
	type AccountData = pallet_balances::AccountData<Balance>;
95
	type OnNewAccount = ();
96
	type OnKilledAccount = ();
97
	type SystemWeightInfo = ();
98
	type BlockWeights = ();
99
	type BlockLength = ();
100
	type SS58Prefix = SS58Prefix;
101
	type OnSetCode = ();
102
	type MaxConsumers = frame_support::traits::ConstU32<16>;
103
	type SingleBlockMigrations = ();
104
	type MultiBlockMigrator = ();
105
	type PreInherents = ();
106
	type PostInherents = ();
107
	type PostTransactions = ();
108
	type ExtensionsWeightInfo = ();
109
}
110
parameter_types! {
111
	pub const ExistentialDeposit: u128 = 0;
112
}
113
impl pallet_balances::Config for Runtime {
114
	type MaxReserves = ();
115
	type ReserveIdentifier = ();
116
	type MaxLocks = ();
117
	type Balance = Balance;
118
	type RuntimeEvent = RuntimeEvent;
119
	type DustRemoval = ();
120
	type ExistentialDeposit = ExistentialDeposit;
121
	type AccountStore = System;
122
	type WeightInfo = ();
123
	type RuntimeHoldReason = ();
124
	type FreezeIdentifier = ();
125
	type MaxFreezes = ();
126
	type RuntimeFreezeReason = ();
127
	type DoneSlashHandler = ();
128
}
129

            
130
// These parameters dont matter much as this will only be called by root with the forced arguments
131
// No deposit is substracted with those methods
132
parameter_types! {
133
	pub const AssetDeposit: Balance = 0;
134
	pub const ApprovalDeposit: Balance = 0;
135
	pub const AssetsStringLimit: u32 = 50;
136
	pub const MetadataDepositBase: Balance = 0;
137
	pub const MetadataDepositPerByte: Balance = 0;
138
}
139

            
140
pub type Precompiles<R> =
141
	PrecompileSetBuilder<R, (PrecompileAt<AddressU64<1>, XtokensPrecompile<R>>,)>;
142

            
143
pub type PCall = XtokensPrecompileCall<Runtime>;
144

            
145
const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;
146
/// Block storage limit in bytes. Set to 40 KB.
147
const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024;
148

            
149
parameter_types! {
150
	pub BlockGasLimit: U256 = U256::from(u64::MAX);
151
	pub PrecompilesValue: Precompiles<Runtime> = Precompiles::new();
152
	pub const WeightPerGas: Weight = Weight::from_parts(1, 0);
153
	pub GasLimitPovSizeRatio: u64 = {
154
		let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
155
		block_gas_limit.saturating_div(MAX_POV_SIZE)
156
	};
157
	pub GasLimitStorageGrowthRatio: u64 = {
158
		let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
159
		block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT)
160
	};
161
}
162

            
163
impl pallet_evm::Config for Runtime {
164
	type FeeCalculator = ();
165
	type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
166
	type WeightPerGas = WeightPerGas;
167
	type CallOrigin = EnsureAddressRoot<AccountId>;
168
	type WithdrawOrigin = EnsureAddressNever<AccountId>;
169
	type AddressMapping = AccountId;
170
	type Currency = Balances;
171
	type Runner = pallet_evm::runner::stack::Runner<Self>;
172
	type PrecompilesType = Precompiles<Self>;
173
	type PrecompilesValue = PrecompilesValue;
174
	type ChainId = ();
175
	type OnChargeTransaction = ();
176
	type BlockGasLimit = BlockGasLimit;
177
	type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping<Self>;
178
	type FindAuthor = ();
179
	type OnCreate = ();
180
	type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
181
	type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio;
182
	type Timestamp = Timestamp;
183
	type WeightInfo = pallet_evm::weights::SubstrateWeight<Runtime>;
184
	type AccountProvider = FrameSystemAccountProvider<Runtime>;
185
	type CreateOriginFilter = ();
186
	type CreateInnerOriginFilter = ();
187
}
188

            
189
parameter_types! {
190
	pub const MinimumPeriod: u64 = 5;
191
}
192
impl pallet_timestamp::Config for Runtime {
193
	type Moment = u64;
194
	type OnTimestampSet = ();
195
	type MinimumPeriod = MinimumPeriod;
196
	type WeightInfo = ();
197
}
198
pub struct ConvertOriginToLocal;
199
impl<Origin: OriginTrait> EnsureOrigin<Origin> for ConvertOriginToLocal {
200
	type Success = Location;
201

            
202
13
	fn try_origin(_: Origin) -> Result<Location, Origin> {
203
13
		Ok(Location::here())
204
13
	}
205

            
206
	#[cfg(feature = "runtime-benchmarks")]
207
	fn try_successful_origin() -> Result<Origin, ()> {
208
		Ok(Origin::root())
209
	}
210
}
211

            
212
pub struct DoNothingRouter;
213
impl SendXcm for DoNothingRouter {
214
	type Ticket = ();
215

            
216
13
	fn validate(
217
13
		_destination: &mut Option<Location>,
218
13
		_message: &mut Option<Xcm<()>>,
219
13
	) -> SendResult<Self::Ticket> {
220
13
		Ok(((), Assets::new()))
221
13
	}
222

            
223
13
	fn deliver(_: Self::Ticket) -> Result<XcmHash, SendError> {
224
13
		Ok(XcmHash::default())
225
13
	}
226
}
227

            
228
pub type Barrier = AllowUnpaidExecutionFrom<Everything>;
229

            
230
pub struct DummyAssetTransactor;
231
impl TransactAsset for DummyAssetTransactor {
232
	fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult {
233
		Ok(())
234
	}
235

            
236
15
	fn withdraw_asset(
237
15
		_what: &Asset,
238
15
		_who: &Location,
239
15
		_maybe_context: Option<&XcmContext>,
240
15
	) -> Result<AssetsInHolding, XcmError> {
241
15
		Ok(AssetsInHolding::default())
242
15
	}
243
}
244

            
245
pub struct DummyWeightTrader;
246
impl WeightTrader for DummyWeightTrader {
247
13
	fn new() -> Self {
248
13
		DummyWeightTrader
249
13
	}
250

            
251
	fn buy_weight(
252
		&mut self,
253
		_weight: Weight,
254
		_payment: AssetsInHolding,
255
		_context: &XcmContext,
256
	) -> Result<AssetsInHolding, XcmError> {
257
		Ok(AssetsInHolding::default())
258
	}
259
}
260

            
261
parameter_types! {
262
	pub UniversalLocation: InteriorLocation = Here;
263
	pub MatcherLocation: Location = Location::here();
264
	pub const MaxAssetsIntoHolding: u32 = 64;
265
}
266

            
267
impl pallet_xcm::Config for Runtime {
268
	// The config types here are entirely configurable, since the only one that is sorely needed
269
	// is `XcmExecutor`, which will be used in unit tests located in xcm-executor.
270
	type RuntimeEvent = RuntimeEvent;
271
	type ExecuteXcmOrigin = ConvertOriginToLocal;
272
	type UniversalLocation = UniversalLocation;
273
	type SendXcmOrigin = ConvertOriginToLocal;
274
	type Weigher = xcm_builder::FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
275
	type XcmRouter = DoNothingRouter;
276
	type XcmExecuteFilter = frame_support::traits::Everything;
277
	type XcmExecutor = xcm_executor::XcmExecutor<XcmConfig>;
278
	type XcmTeleportFilter = frame_support::traits::Everything;
279
	type XcmReserveTransferFilter = frame_support::traits::Everything;
280
	type RuntimeOrigin = RuntimeOrigin;
281
	type RuntimeCall = RuntimeCall;
282
	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
283
	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
284
	type Currency = Balances;
285
	type CurrencyMatcher = IsConcrete<MatcherLocation>;
286
	type TrustedLockers = ();
287
	type SovereignAccountOf = ();
288
	type MaxLockers = ConstU32<8>;
289
	type WeightInfo = pallet_xcm::TestWeightInfo;
290
	type MaxRemoteLockConsumers = ConstU32<0>;
291
	type RemoteLockConsumerIdentifier = ();
292
	type AdminOrigin = frame_system::EnsureRoot<AccountId>;
293
	type AuthorizedAliasConsideration = Disabled;
294
}
295

            
296
#[derive(Encode, Decode)]
297
pub enum RelayCall {
298
	#[codec(index = 5u8)]
299
	// the index should match the position of the module in `construct_runtime!`
300
	Utility(UtilityCall),
301
	#[codec(index = 6u8)]
302
	// the index should match the position of the module in `construct_runtime!`
303
	Hrmp(HrmpCall),
304
}
305

            
306
#[derive(Encode, Decode)]
307
pub enum UtilityCall {
308
	#[codec(index = 1u8)]
309
	AsDerivative(u16),
310
}
311

            
312
// HRMP call encoding, needed for xcm transactor pallet
313
#[derive(Encode, Decode)]
314
pub enum HrmpCall {
315
	#[codec(index = 0u8)]
316
	InitOpenChannel(ParaId, u32, u32),
317
	#[codec(index = 1u8)]
318
	AcceptOpenChannel(ParaId),
319
	#[codec(index = 2u8)]
320
	CloseChannel(HrmpChannelId),
321
	#[codec(index = 6u8)]
322
	CancelOpenRequest(HrmpChannelId, u32),
323
}
324

            
325
#[derive(
326
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
327
)]
328
pub enum MockTransactors {
329
	Relay,
330
}
331

            
332
impl xcm_primitives::XcmTransact for MockTransactors {
333
	fn destination(self) -> Location {
334
		match self {
335
			MockTransactors::Relay => Location::parent(),
336
		}
337
	}
338

            
339
	fn utility_pallet_index(&self) -> u8 {
340
		RelayIndices::<Runtime>::get().utility
341
	}
342

            
343
	fn staking_pallet_index(&self) -> u8 {
344
		RelayIndices::<Runtime>::get().staking
345
	}
346
}
347

            
348
parameter_types! {
349
	pub SelfLocationAbsolute: Location = Location {
350
		parents: 1,
351
		interior: [Parachain(ParachainId::get().into())].into(),
352
	};
353
}
354

            
355
impl pallet_xcm_transactor::Config for Runtime {
356
	type Balance = Balance;
357
	type Transactor = MockTransactors;
358
	type DerivativeAddressRegistrationOrigin = frame_system::EnsureRoot<AccountId>;
359
	type SovereignAccountDispatcherOrigin = frame_system::EnsureRoot<AccountId>;
360
	type CurrencyId = CurrencyId;
361
	type AccountIdToLocation = AccountIdToLocation;
362
	type CurrencyIdToLocation = CurrencyIdToMultiLocation;
363
	type SelfLocation = SelfLocation;
364
	type Weigher = xcm_builder::FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
365
	type UniversalLocation = UniversalLocation;
366
	type BaseXcmWeight = BaseXcmWeight;
367
	type XcmSender = DoNothingRouter;
368
	type AssetTransactor = DummyAssetTransactor;
369
	type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
370
	type WeightInfo = ();
371
	type HrmpManipulatorOrigin = frame_system::EnsureRoot<AccountId>;
372
	type HrmpOpenOrigin = frame_system::EnsureRoot<AccountId>;
373
	type MaxHrmpFee = ();
374
	type FeeTrader = MemoryFeeTrader;
375
}
376

            
377
pub struct XcmConfig;
378
impl xcm_executor::Config for XcmConfig {
379
	type RuntimeCall = RuntimeCall;
380
	type XcmSender = DoNothingRouter;
381
	type AssetTransactor = DummyAssetTransactor;
382
	type OriginConverter = pallet_xcm::XcmPassthrough<RuntimeOrigin>;
383
	type IsReserve = xcm_primitives::MultiNativeAsset<xcm_primitives::RelativeReserveProvider>;
384
	type IsTeleporter = ();
385
	type UniversalLocation = UniversalLocation;
386
	type Barrier = Barrier;
387
	type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
388
	type Trader = DummyWeightTrader;
389
	type ResponseHandler = ();
390
	type SubscriptionService = ();
391
	type AssetTrap = ();
392
	type AssetClaims = ();
393
	type CallDispatcher = RuntimeCall;
394
	type AssetLocker = ();
395
	type AssetExchanger = ();
396
	type PalletInstancesInfo = ();
397
	type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
398
	type FeeManager = ();
399
	type MessageExporter = ();
400
	type UniversalAliases = Nothing;
401
	type SafeCallFilter = Everything;
402
	type Aliasers = Nothing;
403
	type TransactionalProcessor = ();
404
	type HrmpNewChannelOpenRequestHandler = ();
405
	type HrmpChannelAcceptedHandler = ();
406
	type HrmpChannelClosingHandler = ();
407
	type XcmRecorder = ();
408
	type XcmEventEmitter = ();
409
}
410

            
411
#[derive(
412
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
413
)]
414
pub enum CurrencyId {
415
	SelfReserve,
416
	OtherReserve(AssetId),
417
}
418

            
419
// Implement the trait, where we convert AccountId to AssetID
420
impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
421
	/// The way to convert an account to assetId is by ensuring that the prefix is 0XFFFFFFFF
422
	/// and by taking the lowest 128 bits as the assetId
423
8
	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
424
8
		match account {
425
8
			a if a.has_prefix_u32(0xffffffff) => Some(CurrencyId::OtherReserve(a.without_prefix())),
426
1
			a if a == AccountId::from(SelfReserveAccount) => Some(CurrencyId::SelfReserve),
427
			_ => None,
428
		}
429
8
	}
430
}
431

            
432
pub struct CurrencyIdToMultiLocation;
433

            
434
impl sp_runtime::traits::Convert<CurrencyId, Option<Location>> for CurrencyIdToMultiLocation {
435
8
	fn convert(currency: CurrencyId) -> Option<Location> {
436
8
		match currency {
437
			CurrencyId::SelfReserve => {
438
1
				let multi: Location = SelfReserve::get();
439
1
				Some(multi)
440
			}
441
			// To distinguish between relay and others, specially for reserve asset
442
7
			CurrencyId::OtherReserve(asset) => {
443
7
				if asset == 0 {
444
3
					Some(Location::parent())
445
				} else {
446
4
					Some(Location::new(1, [Parachain(2), GeneralIndex(asset)]))
447
				}
448
			}
449
		}
450
8
	}
451
}
452

            
453
pub struct AccountIdToLocation;
454
impl sp_runtime::traits::Convert<AccountId, Location> for AccountIdToLocation {
455
	fn convert(account: AccountId) -> Location {
456
		let as_h160: H160 = account.into();
457
		Location::new(
458
			1,
459
			[AccountKey20 {
460
				network: None,
461
				key: as_h160.as_fixed_bytes().clone(),
462
			}],
463
		)
464
	}
465
}
466

            
467
parameter_types! {
468
	pub Ancestry: Location = Parachain(ParachainId::get().into()).into();
469

            
470
	pub const BaseXcmWeight: Weight = Weight::from_parts(1000u64, 1000u64);
471
	pub const RelayNetwork: NetworkId = NetworkId::Polkadot;
472
	pub const MaxAssetsForTransfer: usize = 2;
473

            
474
	pub SelfLocation: Location =
475
		Location::new(1, [Parachain(ParachainId::get().into())]);
476

            
477
	pub SelfReserve: Location = Location::new(
478
		1,
479
		[
480
			Parachain(ParachainId::get().into()),
481
			PalletInstance(
482
				<Runtime as frame_system::Config>::PalletInfo::index::<Balances>().unwrap() as u8
483
			)
484
		]);
485
	pub MaxInstructions: u32 = 100;
486
}
487

            
488
pub(crate) struct ExtBuilder {
489
	// endowed accounts with balances
490
	balances: Vec<(AccountId, Balance)>,
491
}
492

            
493
impl Default for ExtBuilder {
494
19
	fn default() -> ExtBuilder {
495
19
		ExtBuilder { balances: vec![] }
496
19
	}
497
}
498

            
499
impl ExtBuilder {
500
16
	pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
501
16
		self.balances = balances;
502
16
		self
503
16
	}
504
19
	pub(crate) fn build(self) -> sp_io::TestExternalities {
505
19
		let mut t = frame_system::GenesisConfig::<Runtime>::default()
506
19
			.build_storage()
507
19
			.expect("Frame system builds valid default genesis config");
508

            
509
19
		pallet_balances::GenesisConfig::<Runtime> {
510
19
			balances: self.balances,
511
19
			dev_accounts: None,
512
19
		}
513
19
		.assimilate_storage(&mut t)
514
19
		.expect("Pallet balances storage can be assimilated");
515

            
516
19
		let mut ext = sp_io::TestExternalities::new(t);
517
19
		ext.execute_with(|| System::set_block_number(1));
518
19
		ext
519
19
	}
520
}
521

            
522
14
pub(crate) fn events() -> Vec<RuntimeEvent> {
523
14
	System::events()
524
14
		.into_iter()
525
14
		.map(|r| r.event)
526
14
		.collect::<Vec<_>>()
527
14
}