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
//! Testing utilities.
18

            
19
use super::*;
20

            
21
use frame_support::{
22
	construct_runtime, parameter_types,
23
	traits::{AsEnsureOriginWithArg, Everything},
24
	weights::Weight,
25
};
26

            
27
use frame_system::{EnsureNever, EnsureRoot};
28
use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, FrameSystemAccountProvider};
29
use precompile_utils::{
30
	mock_account,
31
	precompile_set::*,
32
	testing::{AddressInPrefixedSet, MockAccount},
33
};
34
use sp_core::H256;
35
use sp_runtime::{
36
	traits::{BlakeTwo256, ConstU32, IdentityLookup},
37
	BuildStorage,
38
};
39

            
40
pub type AccountId = MockAccount;
41
pub type AssetId = u128;
42
pub type Balance = u128;
43
pub type Block = frame_system::mocking::MockBlockU32<Runtime>;
44

            
45
/// The foreign asset precompile address prefix. Addresses that match against this prefix will
46
/// be routed to Erc20AssetsPrecompileSet being marked as foreign
47
pub const FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX: u32 = 0xffffffff;
48

            
49
parameter_types! {
50
	pub ForeignAssetPrefix: &'static [u8] = &[0xff, 0xff, 0xff, 0xff];
51
}
52

            
53
78
mock_account!(ForeignAssetId(AssetId), |value: ForeignAssetId| {
54
78
	AddressInPrefixedSet(FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, value.0).into()
55
78
});
56

            
57
// Implement the trait, where we convert AccountId to AssetID
58
impl AccountIdAssetIdConversion<AccountId, AssetId> for Runtime {
59
	/// The way to convert an account to assetId is by ensuring that the prefix is 0XFFFFFFFF
60
	/// and by taking the lowest 128 bits as the assetId
61
174
	fn account_to_asset_id(account: AccountId) -> Option<(Vec<u8>, AssetId)> {
62
174
		if account.has_prefix_u32(FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX) {
63
174
			return Some((
64
174
				FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX
65
174
					.to_be_bytes()
66
174
					.to_vec(),
67
174
				account.without_prefix(),
68
174
			));
69
		}
70
		None
71
174
	}
72

            
73
	// Not used for now
74
	fn asset_id_to_account(_prefix: &[u8], asset_id: AssetId) -> AccountId {
75
		ForeignAssetId(asset_id).into()
76
	}
77
}
78

            
79
parameter_types! {
80
	pub const BlockHashCount: u32 = 250;
81
	pub const SS58Prefix: u8 = 42;
82
}
83

            
84
impl frame_system::Config for Runtime {
85
	type BaseCallFilter = Everything;
86
	type DbWeight = ();
87
	type RuntimeOrigin = RuntimeOrigin;
88
	type RuntimeTask = RuntimeTask;
89
	type Nonce = u64;
90
	type Block = Block;
91
	type RuntimeCall = RuntimeCall;
92
	type Hash = H256;
93
	type Hashing = BlakeTwo256;
94
	type AccountId = AccountId;
95
	type Lookup = IdentityLookup<Self::AccountId>;
96
	type RuntimeEvent = RuntimeEvent;
97
	type BlockHashCount = BlockHashCount;
98
	type Version = ();
99
	type PalletInfo = PalletInfo;
100
	type AccountData = pallet_balances::AccountData<Balance>;
101
	type OnNewAccount = ();
102
	type OnKilledAccount = ();
103
	type SystemWeightInfo = ();
104
	type BlockWeights = ();
105
	type BlockLength = ();
106
	type SS58Prefix = SS58Prefix;
107
	type OnSetCode = ();
108
	type MaxConsumers = frame_support::traits::ConstU32<16>;
109
	type SingleBlockMigrations = ();
110
	type MultiBlockMigrator = ();
111
	type PreInherents = ();
112
	type PostInherents = ();
113
	type PostTransactions = ();
114
	type ExtensionsWeightInfo = ();
115
}
116

            
117
parameter_types! {
118
	pub const MinimumPeriod: u64 = 5;
119
}
120

            
121
impl pallet_timestamp::Config for Runtime {
122
	type Moment = u64;
123
	type OnTimestampSet = ();
124
	type MinimumPeriod = MinimumPeriod;
125
	type WeightInfo = ();
126
}
127

            
128
parameter_types! {
129
	pub const ExistentialDeposit: u128 = 0;
130
}
131

            
132
impl pallet_balances::Config for Runtime {
133
	type MaxReserves = ();
134
	type ReserveIdentifier = ();
135
	type MaxLocks = ();
136
	type Balance = Balance;
137
	type RuntimeEvent = RuntimeEvent;
138
	type DustRemoval = ();
139
	type ExistentialDeposit = ExistentialDeposit;
140
	type AccountStore = System;
141
	type WeightInfo = ();
142
	type RuntimeHoldReason = ();
143
	type FreezeIdentifier = ();
144
	type MaxFreezes = ();
145
	type RuntimeFreezeReason = ();
146
	type DoneSlashHandler = ();
147
}
148

            
149
pub type Precompiles<R> = PrecompileSetBuilder<
150
	R,
151
	(
152
		PrecompileSetStartingWith<
153
			ForeignAssetPrefix,
154
			Erc20AssetsPrecompileSet<R, pallet_assets::Instance1>,
155
		>,
156
	),
157
>;
158

            
159
pub type ForeignPCall = Erc20AssetsPrecompileSetCall<Runtime, pallet_assets::Instance1>;
160

            
161
const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;
162
/// Block Storage Limit in bytes. Set to 40KB.
163
const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024;
164

            
165
parameter_types! {
166
	pub BlockGasLimit: U256 = U256::from(u64::MAX);
167
	pub PrecompilesValue: Precompiles<Runtime> = Precompiles::new();
168
	pub WeightPerGas: Weight = Weight::from_parts(1, 0);
169
	pub GasLimitPovSizeRatio: u64 = {
170
		let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
171
		block_gas_limit.saturating_div(MAX_POV_SIZE)
172
	};
173
	pub GasLimitStorageGrowthRatio: u64 = {
174
		let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
175
		block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT)
176
	};
177
}
178

            
179
impl pallet_evm::Config for Runtime {
180
	type FeeCalculator = ();
181
	type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
182
	type WeightPerGas = WeightPerGas;
183
	type CallOrigin = EnsureAddressRoot<AccountId>;
184
	type WithdrawOrigin = EnsureAddressNever<AccountId>;
185
	type AddressMapping = AccountId;
186
	type Currency = Balances;
187
	type RuntimeEvent = RuntimeEvent;
188
	type Runner = pallet_evm::runner::stack::Runner<Self>;
189
	type PrecompilesType = Precompiles<Self>;
190
	type PrecompilesValue = PrecompilesValue;
191
	type ChainId = ();
192
	type OnChargeTransaction = ();
193
	type BlockGasLimit = BlockGasLimit;
194
	type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping<Self>;
195
	type FindAuthor = ();
196
	type OnCreate = ();
197
	type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
198
	type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio;
199
	type Timestamp = Timestamp;
200
	type WeightInfo = pallet_evm::weights::SubstrateWeight<Runtime>;
201
	type AccountProvider = FrameSystemAccountProvider<Runtime>;
202
}
203

            
204
type ForeignAssetInstance = pallet_assets::Instance1;
205

            
206
// Required for runtime benchmarks
207
pallet_assets::runtime_benchmarks_enabled! {
208
	pub struct BenchmarkHelper;
209
	impl<AssetIdParameter> pallet_assets::BenchmarkHelper<AssetIdParameter> for BenchmarkHelper
210
	where
211
		AssetIdParameter: From<u128>,
212
	{
213
		fn create_asset_id_parameter(id: u32) -> AssetIdParameter {
214
			(id as u128).into()
215
		}
216
	}
217
}
218

            
219
// These parameters dont matter much as this will only be called by root with the forced arguments
220
// No deposit is substracted with those methods
221
parameter_types! {
222
	pub const AssetDeposit: Balance = 0;
223
	pub const ApprovalDeposit: Balance = 0;
224
	pub const AssetsStringLimit: u32 = 50;
225
	pub const MetadataDepositBase: Balance = 0;
226
	pub const MetadataDepositPerByte: Balance = 0;
227
	pub const AssetAccountDeposit: Balance = 0;
228
}
229

            
230
impl pallet_assets::Config<ForeignAssetInstance> for Runtime {
231
	type RuntimeEvent = RuntimeEvent;
232
	type Balance = Balance;
233
	type AssetId = AssetId;
234
	type Currency = Balances;
235
	type ForceOrigin = EnsureRoot<AccountId>;
236
	type AssetDeposit = AssetDeposit;
237
	type MetadataDepositBase = MetadataDepositBase;
238
	type MetadataDepositPerByte = MetadataDepositPerByte;
239
	type ApprovalDeposit = ApprovalDeposit;
240
	type StringLimit = AssetsStringLimit;
241
	type Freezer = ();
242
	type Extra = ();
243
	type AssetAccountDeposit = AssetAccountDeposit;
244
	type WeightInfo = pallet_assets::weights::SubstrateWeight<Runtime>;
245
	type RemoveItemsLimit = ConstU32<656>;
246
	type AssetIdParameter = AssetId;
247
	type CreateOrigin = AsEnsureOriginWithArg<EnsureNever<AccountId>>;
248
	type CallbackHandle = ();
249
	pallet_assets::runtime_benchmarks_enabled! {
250
		type BenchmarkHelper = BenchmarkHelper;
251
	}
252
}
253

            
254
// Configure a mock runtime to test the pallet.
255
1533
construct_runtime!(
256
492
	pub enum Runtime
257
492
	{
258
492
		System: frame_system,
259
492
		Balances: pallet_balances,
260
492
		ForeignAssets: pallet_assets::<Instance1>,
261
492
		Evm: pallet_evm,
262
492
		Timestamp: pallet_timestamp,
263
492
	}
264
1610
);
265

            
266
pub(crate) struct ExtBuilder {
267
	// endowed accounts with balances
268
	balances: Vec<(AccountId, Balance)>,
269
}
270

            
271
impl Default for ExtBuilder {
272
29
	fn default() -> ExtBuilder {
273
29
		ExtBuilder { balances: vec![] }
274
29
	}
275
}
276

            
277
impl ExtBuilder {
278
27
	pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
279
27
		self.balances = balances;
280
27
		self
281
27
	}
282

            
283
29
	pub(crate) fn build(self) -> sp_io::TestExternalities {
284
29
		let mut t = frame_system::GenesisConfig::<Runtime>::default()
285
29
			.build_storage()
286
29
			.expect("Frame system builds valid default genesis config");
287
29

            
288
29
		pallet_balances::GenesisConfig::<Runtime> {
289
29
			balances: self.balances,
290
29
		}
291
29
		.assimilate_storage(&mut t)
292
29
		.expect("Pallet balances storage can be assimilated");
293
29

            
294
29
		let mut ext = sp_io::TestExternalities::new(t);
295
29
		ext.execute_with(|| System::set_block_number(1));
296
29
		ext
297
29
	}
298
}