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
extern crate alloc;
18

            
19
use crate::{
20
	currency::GLMR, currency::SUPPLY_FACTOR, AccountId, AuthorFilterConfig, AuthorMappingConfig,
21
	Balance, Balances, BalancesConfig, BridgeKusamaGrandpaConfig, BridgeKusamaMessagesConfig,
22
	BridgeKusamaParachainsConfig, BridgeXcmOverMoonriverConfig, CrowdloanRewardsConfig, EVMConfig,
23
	EligibilityValue, EthereumChainIdConfig, EthereumConfig, EvmForeignAssetsConfig, InflationInfo,
24
	MaintenanceModeConfig, OpenTechCommitteeCollectiveConfig, ParachainInfoConfig,
25
	ParachainStakingConfig, PolkadotXcmConfig, Precompiles, Range, RuntimeGenesisConfig,
26
	TransactionPaymentConfig, TreasuryCouncilCollectiveConfig, XcmWeightTraderConfig, HOURS,
27
};
28
use alloc::{vec, vec::Vec};
29
use bp_messages::MessagesOperatingMode;
30
use bp_runtime::BasicOperatingMode;
31
use cumulus_primitives_core::ParaId;
32
use fp_evm::GenesisAccount;
33
use frame_support::pallet_prelude::PalletInfoAccess;
34
use nimbus_primitives::NimbusId;
35
use pallet_moonbeam_foreign_assets::EvmForeignAssetInfo;
36
use pallet_transaction_payment::Multiplier;
37
use pallet_xcm_weight_trader::XcmWeightTraderAssetInfo;
38
use sp_genesis_builder::PresetId;
39
use sp_keyring::Sr25519Keyring;
40
use sp_runtime::{Perbill, Percent};
41
use xcm::prelude::{GlobalConsensus, Junctions, Location, NetworkId, PalletInstance, Parachain};
42

            
43
const COLLATOR_COMMISSION: Perbill = Perbill::from_percent(20);
44
const PARACHAIN_BOND_RESERVE_PERCENT: Percent = Percent::from_percent(30);
45
const BLOCKS_PER_ROUND: u32 = 6 * HOURS;
46
const BLOCKS_PER_YEAR: u32 = 31_557_600 / 12;
47
const NUM_SELECTED_CANDIDATES: u32 = 8;
48

            
49
pub fn moonbeam_inflation_config() -> InflationInfo<Balance> {
50
	fn to_round_inflation(annual: Range<Perbill>) -> Range<Perbill> {
51
		use pallet_parachain_staking::inflation::perbill_annual_to_perbill_round;
52
		perbill_annual_to_perbill_round(
53
			annual,
54
			// rounds per year
55
			BLOCKS_PER_YEAR / BLOCKS_PER_ROUND,
56
		)
57
	}
58
	let annual = Range {
59
		min: Perbill::from_percent(4),
60
		ideal: Perbill::from_percent(5),
61
		max: Perbill::from_percent(5),
62
	};
63
	InflationInfo {
64
		// staking expectations
65
		expect: Range {
66
			min: 100_000 * GLMR * SUPPLY_FACTOR,
67
			ideal: 200_000 * GLMR * SUPPLY_FACTOR,
68
			max: 500_000 * GLMR * SUPPLY_FACTOR,
69
		},
70
		// annual inflation
71
		annual,
72
		round: to_round_inflation(annual),
73
	}
74
}
75

            
76
pub fn testnet_genesis(
77
	treasury_council_members: Vec<AccountId>,
78
	open_tech_committee_members: Vec<AccountId>,
79
	candidates: Vec<(AccountId, NimbusId, Balance)>,
80
	delegations: Vec<(AccountId, AccountId, Balance, Percent)>,
81
	endowed_accounts: Vec<AccountId>,
82
	crowdloan_fund_pot: Balance,
83
	para_id: ParaId,
84
	chain_id: u64,
85
) -> serde_json::Value {
86
	// This is the simplest bytecode to revert without returning any data.
87
	// We will pre-deploy it under all of our precompiles to ensure they can be called from
88
	// within contracts.
89
	// (PUSH1 0x00 PUSH1 0x00 REVERT)
90
	let revert_bytecode = vec![0x60, 0x00, 0x60, 0x00, 0xFD];
91

            
92
	let config = RuntimeGenesisConfig {
93
		system: Default::default(),
94
		balances: BalancesConfig {
95
			balances: endowed_accounts
96
				.iter()
97
				.cloned()
98
				.map(|k| (k, 1 << 110))
99
				.collect(),
100
			dev_accounts: Default::default(),
101
		},
102
		crowdloan_rewards: CrowdloanRewardsConfig {
103
			funded_amount: crowdloan_fund_pot,
104
		},
105
		parachain_info: ParachainInfoConfig {
106
			parachain_id: para_id,
107
			..Default::default()
108
		},
109
		ethereum_chain_id: EthereumChainIdConfig {
110
			chain_id,
111
			..Default::default()
112
		},
113
		evm: EVMConfig {
114
			// We need _some_ code inserted at the precompile address so that
115
			// the evm will actually call the address.
116
			accounts: Precompiles::used_addresses()
117
				.map(|addr| {
118
					(
119
						addr.into(),
120
						GenesisAccount {
121
							nonce: Default::default(),
122
							balance: Default::default(),
123
							storage: Default::default(),
124
							code: revert_bytecode.clone(),
125
						},
126
					)
127
				})
128
				.collect(),
129
			..Default::default()
130
		},
131
		ethereum: EthereumConfig {
132
			..Default::default()
133
		},
134
		parachain_staking: ParachainStakingConfig {
135
			candidates: candidates
136
				.iter()
137
				.cloned()
138
				.map(|(account, _, bond)| (account, bond))
139
				.collect(),
140
			delegations,
141
			inflation_config: moonbeam_inflation_config(),
142
			collator_commission: COLLATOR_COMMISSION,
143
			parachain_bond_reserve_percent: PARACHAIN_BOND_RESERVE_PERCENT,
144
			blocks_per_round: BLOCKS_PER_ROUND,
145
			num_selected_candidates: NUM_SELECTED_CANDIDATES,
146
		},
147
		treasury_council_collective: TreasuryCouncilCollectiveConfig {
148
			phantom: Default::default(),
149
			members: treasury_council_members,
150
		},
151
		open_tech_committee_collective: OpenTechCommitteeCollectiveConfig {
152
			phantom: Default::default(),
153
			members: open_tech_committee_members,
154
		},
155
		author_filter: AuthorFilterConfig {
156
			eligible_count: EligibilityValue::new_unchecked(50),
157
			..Default::default()
158
		},
159
		author_mapping: AuthorMappingConfig {
160
			mappings: candidates
161
				.iter()
162
				.cloned()
163
				.map(|(account_id, author_id, _)| (author_id, account_id))
164
				.collect(),
165
		},
166
		proxy_genesis_companion: Default::default(),
167
		treasury: Default::default(),
168
		maintenance_mode: MaintenanceModeConfig {
169
			start_in_maintenance_mode: false,
170
			..Default::default()
171
		},
172
		polkadot_xcm: PolkadotXcmConfig {
173
			supported_version: vec![
174
				// Required for bridging Moonbeam with Moonriver
175
				(
176
					bp_moonriver::GlobalConsensusLocation::get(),
177
					xcm::latest::VERSION,
178
				),
179
			],
180
			..Default::default()
181
		},
182
		transaction_payment: TransactionPaymentConfig {
183
			multiplier: Multiplier::from(8u128),
184
			..Default::default()
185
		},
186
		evm_foreign_assets: EvmForeignAssetsConfig {
187
			assets: vec![EvmForeignAssetInfo {
188
				asset_id: 1001,
189
				name: b"xcMOVR".to_vec().try_into().expect("Invalid asset name"),
190
				symbol: b"xcMOVR".to_vec().try_into().expect("Invalid asset symbol"),
191
				decimals: 18,
192
				xcm_location: Location::new(
193
					2,
194
					[
195
						GlobalConsensus(crate::bridge_config::KusamaGlobalConsensusNetwork::get()),
196
						Parachain(<bp_moonriver::Moonriver as bp_runtime::Parachain>::PARACHAIN_ID),
197
						PalletInstance(<Balances as PalletInfoAccess>::index() as u8),
198
					],
199
				),
200
			}],
201
			_phantom: Default::default(),
202
		},
203
		xcm_weight_trader: XcmWeightTraderConfig {
204
			assets: vec![XcmWeightTraderAssetInfo {
205
				location: Location::new(
206
					2,
207
					[
208
						GlobalConsensus(crate::bridge_config::KusamaGlobalConsensusNetwork::get()),
209
						Parachain(<bp_moonriver::Moonriver as bp_runtime::Parachain>::PARACHAIN_ID),
210
						PalletInstance(<Balances as PalletInfoAccess>::index() as u8),
211
					],
212
				),
213
				relative_price: GLMR,
214
			}],
215
			_phantom: Default::default(),
216
		},
217
		bridge_kusama_grandpa: BridgeKusamaGrandpaConfig {
218
			owner: Some(endowed_accounts[0]),
219
			init_data: None,
220
		},
221
		bridge_kusama_parachains: BridgeKusamaParachainsConfig {
222
			owner: Some(endowed_accounts[0]),
223
			operating_mode: BasicOperatingMode::Normal,
224
			..Default::default()
225
		},
226
		bridge_kusama_messages: BridgeKusamaMessagesConfig {
227
			owner: Some(endowed_accounts[0]),
228
			opened_lanes: vec![],
229
			operating_mode: MessagesOperatingMode::Basic(BasicOperatingMode::Normal),
230
			_phantom: Default::default(),
231
		},
232
		bridge_xcm_over_moonriver: BridgeXcmOverMoonriverConfig {
233
			opened_bridges: vec![(
234
				Location::new(
235
					1,
236
					[Parachain(
237
						<bp_moonbeam::Moonbeam as bp_runtime::Parachain>::PARACHAIN_ID,
238
					)],
239
				),
240
				Junctions::from([
241
					NetworkId::Kusama.into(),
242
					Parachain(<bp_moonriver::Moonriver as bp_runtime::Parachain>::PARACHAIN_ID),
243
				]),
244
				Some(Default::default()),
245
			)],
246
			_phantom: Default::default(),
247
		},
248
	};
249

            
250
	serde_json::to_value(&config).expect("Could not build genesis config.")
251
}
252

            
253
/// Generate a chain spec for use with the development service.
254
pub fn development() -> serde_json::Value {
255
	testnet_genesis(
256
		// Treasury Council members: Baltathar, Charleth and Dorothy
257
		vec![
258
			AccountId::from(sp_core::hex2array!(
259
				"3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"
260
			)),
261
			AccountId::from(sp_core::hex2array!(
262
				"798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc"
263
			)),
264
			AccountId::from(sp_core::hex2array!(
265
				"773539d4Ac0e786233D90A233654ccEE26a613D9"
266
			)),
267
		],
268
		// Open Tech committee members: Alith and Baltathar
269
		vec![
270
			AccountId::from(sp_core::hex2array!(
271
				"f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
272
			)),
273
			AccountId::from(sp_core::hex2array!(
274
				"3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"
275
			)),
276
		],
277
		// Collator Candidate: Alice -> Alith
278
		vec![(
279
			AccountId::from(sp_core::hex2array!(
280
				"f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
281
			)),
282
			NimbusId::from(Sr25519Keyring::Alice.public()),
283
			20_000 * GLMR * SUPPLY_FACTOR,
284
		)],
285
		// Delegations
286
		vec![],
287
		vec![
288
			AccountId::from(sp_core::hex2array!(
289
				"f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
290
			)),
291
			AccountId::from(sp_core::hex2array!(
292
				"3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"
293
			)),
294
			AccountId::from(sp_core::hex2array!(
295
				"798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc"
296
			)),
297
			AccountId::from(sp_core::hex2array!(
298
				"773539d4Ac0e786233D90A233654ccEE26a613D9"
299
			)),
300
		],
301
		1_500_000 * GLMR * SUPPLY_FACTOR,
302
		Default::default(), // para_id
303
		1281,               //ChainId
304
	)
305
}
306

            
307
/// Provides the JSON representation of predefined genesis config for given `id`.
308
pub fn get_preset(id: &PresetId) -> Option<Vec<u8>> {
309
	let patch = match id.as_str() {
310
		sp_genesis_builder::DEV_RUNTIME_PRESET => development(),
311
		_ => return None,
312
	};
313
	Some(
314
		serde_json::to_string(&patch)
315
			.expect("serialization to json is expected to work. qed.")
316
			.into_bytes(),
317
	)
318
}
319

            
320
/// List of supported presets.
321
pub fn preset_names() -> Vec<PresetId> {
322
	vec![PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET)]
323
}