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, BalancesConfig, CrowdloanRewardsConfig, EVMConfig, EligibilityValue,
22
	EthereumChainIdConfig, EthereumConfig, InflationInfo, MaintenanceModeConfig,
23
	OpenTechCommitteeCollectiveConfig, ParachainInfoConfig, ParachainStakingConfig,
24
	PolkadotXcmConfig, Precompiles, Range, RuntimeGenesisConfig, TransactionPaymentConfig,
25
	TreasuryCouncilCollectiveConfig, HOURS,
26
};
27
use alloc::{vec, vec::Vec};
28
use cumulus_primitives_core::ParaId;
29
use fp_evm::GenesisAccount;
30
use nimbus_primitives::NimbusId;
31
use pallet_transaction_payment::Multiplier;
32
use sp_genesis_builder::PresetId;
33
use sp_keyring::Sr25519Keyring;
34
use sp_runtime::{Perbill, Percent};
35

            
36
const COLLATOR_COMMISSION: Perbill = Perbill::from_percent(20);
37
const PARACHAIN_BOND_RESERVE_PERCENT: Percent = Percent::from_percent(30);
38
const BLOCKS_PER_ROUND: u32 = 6 * HOURS;
39
const BLOCKS_PER_YEAR: u32 = 31_557_600 / 12;
40
const NUM_SELECTED_CANDIDATES: u32 = 8;
41

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

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

            
85
	let config = RuntimeGenesisConfig {
86
		system: Default::default(),
87
		balances: BalancesConfig {
88
			balances: endowed_accounts
89
				.iter()
90
				.cloned()
91
				.map(|k| (k, 1 << 110))
92
				.collect(),
93
		},
94
		crowdloan_rewards: CrowdloanRewardsConfig {
95
			funded_amount: crowdloan_fund_pot,
96
		},
97
		parachain_info: ParachainInfoConfig {
98
			parachain_id: para_id,
99
			..Default::default()
100
		},
101
		ethereum_chain_id: EthereumChainIdConfig {
102
			chain_id,
103
			..Default::default()
104
		},
105
		evm: EVMConfig {
106
			// We need _some_ code inserted at the precompile address so that
107
			// the evm will actually call the address.
108
			accounts: Precompiles::used_addresses()
109
				.map(|addr| {
110
					(
111
						addr.into(),
112
						GenesisAccount {
113
							nonce: Default::default(),
114
							balance: Default::default(),
115
							storage: Default::default(),
116
							code: revert_bytecode.clone(),
117
						},
118
					)
119
				})
120
				.collect(),
121
			..Default::default()
122
		},
123
		ethereum: EthereumConfig {
124
			..Default::default()
125
		},
126
		parachain_staking: ParachainStakingConfig {
127
			candidates: candidates
128
				.iter()
129
				.cloned()
130
				.map(|(account, _, bond)| (account, bond))
131
				.collect(),
132
			delegations,
133
			inflation_config: moonbeam_inflation_config(),
134
			collator_commission: COLLATOR_COMMISSION,
135
			parachain_bond_reserve_percent: PARACHAIN_BOND_RESERVE_PERCENT,
136
			blocks_per_round: BLOCKS_PER_ROUND,
137
			num_selected_candidates: NUM_SELECTED_CANDIDATES,
138
		},
139
		treasury_council_collective: TreasuryCouncilCollectiveConfig {
140
			phantom: Default::default(),
141
			members: treasury_council_members,
142
		},
143
		open_tech_committee_collective: OpenTechCommitteeCollectiveConfig {
144
			phantom: Default::default(),
145
			members: open_tech_committee_members,
146
		},
147
		author_filter: AuthorFilterConfig {
148
			eligible_count: EligibilityValue::new_unchecked(50),
149
			..Default::default()
150
		},
151
		author_mapping: AuthorMappingConfig {
152
			mappings: candidates
153
				.iter()
154
				.cloned()
155
				.map(|(account_id, author_id, _)| (author_id, account_id))
156
				.collect(),
157
		},
158
		proxy_genesis_companion: Default::default(),
159
		treasury: Default::default(),
160
		migrations: Default::default(),
161
		maintenance_mode: MaintenanceModeConfig {
162
			start_in_maintenance_mode: false,
163
			..Default::default()
164
		},
165
		// This should initialize it to whatever we have set in the pallet
166
		polkadot_xcm: PolkadotXcmConfig::default(),
167
		transaction_payment: TransactionPaymentConfig {
168
			multiplier: Multiplier::from(8u128),
169
			..Default::default()
170
		},
171
	};
172

            
173
	serde_json::to_value(&config).expect("Could not build genesis config.")
174
}
175

            
176
/// Generate a chain spec for use with the development service.
177
pub fn development() -> serde_json::Value {
178
	testnet_genesis(
179
		// Treasury Council members: Baltathar, Charleth and Dorothy
180
		vec![
181
			AccountId::from(sp_core::hex2array!(
182
				"3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"
183
			)),
184
			AccountId::from(sp_core::hex2array!(
185
				"798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc"
186
			)),
187
			AccountId::from(sp_core::hex2array!(
188
				"773539d4Ac0e786233D90A233654ccEE26a613D9"
189
			)),
190
		],
191
		// Open Tech committee members: Alith and Baltathar
192
		vec![
193
			AccountId::from(sp_core::hex2array!(
194
				"f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
195
			)),
196
			AccountId::from(sp_core::hex2array!(
197
				"3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"
198
			)),
199
		],
200
		// Collator Candidate: Alice -> Alith
201
		vec![(
202
			AccountId::from(sp_core::hex2array!(
203
				"f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
204
			)),
205
			NimbusId::from(Sr25519Keyring::Alice.public()),
206
			20_000 * GLMR * SUPPLY_FACTOR,
207
		)],
208
		// Delegations
209
		vec![],
210
		vec![
211
			AccountId::from(sp_core::hex2array!(
212
				"f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
213
			)),
214
			AccountId::from(sp_core::hex2array!(
215
				"3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"
216
			)),
217
			AccountId::from(sp_core::hex2array!(
218
				"798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc"
219
			)),
220
			AccountId::from(sp_core::hex2array!(
221
				"773539d4Ac0e786233D90A233654ccEE26a613D9"
222
			)),
223
		],
224
		1_500_000 * GLMR * SUPPLY_FACTOR,
225
		Default::default(), // para_id
226
		1281,               //ChainId
227
	)
228
}
229

            
230
/// Provides the JSON representation of predefined genesis config for given `id`.
231
pub fn get_preset(id: &PresetId) -> Option<Vec<u8>> {
232
	let patch = match id.as_str() {
233
		sp_genesis_builder::DEV_RUNTIME_PRESET => development(),
234
		_ => return None,
235
	};
236
	Some(
237
		serde_json::to_string(&patch)
238
			.expect("serialization to json is expected to work. qed.")
239
			.into_bytes(),
240
	)
241
}
242

            
243
/// List of supported presets.
244
pub fn preset_names() -> Vec<PresetId> {
245
	vec![PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET)]
246
}