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 Chain Specifications and utilities for building them.
18
//!
19
//! Learn more about Substrate chain specifications at
20
//! https://substrate.dev/docs/en/knowledgebase/integrate/chain-spec
21
extern crate alloc;
22

            
23
use crate::{
24
	currency::MOVR, xcm_config::Transactors, AccountId, AuthorFilterConfig, AuthorMappingConfig,
25
	Balance, Balances, BalancesConfig, BridgePolkadotGrandpaConfig, BridgePolkadotMessagesConfig,
26
	BridgePolkadotParachainsConfig, BridgeXcmOverMoonbeamConfig, EVMConfig, EligibilityValue,
27
	EthereumChainIdConfig, EthereumConfig, EvmForeignAssetsConfig, InflationInfo,
28
	MaintenanceModeConfig, OpenTechCommitteeCollectiveConfig, ParachainInfoConfig,
29
	ParachainStakingConfig, PolkadotXcmConfig, Precompiles, Range, RuntimeGenesisConfig,
30
	TransactionPaymentConfig, TreasuryCouncilCollectiveConfig, XcmTransactorConfig,
31
	XcmWeightTraderConfig, HOURS,
32
};
33
use alloc::{vec, vec::Vec};
34
use bp_messages::MessagesOperatingMode;
35
use bp_runtime::BasicOperatingMode;
36
use cumulus_primitives_core::ParaId;
37
use fp_evm::GenesisAccount;
38
use frame_support::pallet_prelude::PalletInfoAccess;
39
use nimbus_primitives::NimbusId;
40
use pallet_moonbeam_foreign_assets::EvmForeignAssetInfo;
41
use pallet_transaction_payment::Multiplier;
42
use pallet_xcm_weight_trader::XcmWeightTraderAssetInfo;
43
use sp_genesis_builder::PresetId;
44
use sp_keyring::Sr25519Keyring;
45
use sp_runtime::{Perbill, Percent};
46
use xcm::latest::{Junctions, Location, NetworkId};
47
use xcm::prelude::{GlobalConsensus, PalletInstance, Parachain};
48

            
49
const COLLATOR_COMMISSION: Perbill = Perbill::from_percent(20);
50
const PARACHAIN_BOND_RESERVE_PERCENT: Percent = Percent::from_percent(30);
51
const BLOCKS_PER_ROUND: u32 = 2 * HOURS;
52
const BLOCKS_PER_YEAR: u32 = 31_557_600 / 12;
53
const NUM_SELECTED_CANDIDATES: u32 = 8;
54

            
55
pub fn moonriver_inflation_config() -> InflationInfo<Balance> {
56
	fn to_round_inflation(annual: Range<Perbill>) -> Range<Perbill> {
57
		use pallet_parachain_staking::inflation::perbill_annual_to_perbill_round;
58
		perbill_annual_to_perbill_round(
59
			annual,
60
			// rounds per year
61
			BLOCKS_PER_YEAR / BLOCKS_PER_ROUND,
62
		)
63
	}
64
	let annual = Range {
65
		min: Perbill::from_percent(4),
66
		ideal: Perbill::from_percent(5),
67
		max: Perbill::from_percent(5),
68
	};
69
	InflationInfo {
70
		// staking expectations
71
		expect: Range {
72
			min: 100_000 * MOVR,
73
			ideal: 200_000 * MOVR,
74
			max: 500_000 * MOVR,
75
		},
76
		// annual inflation
77
		annual,
78
		round: to_round_inflation(annual),
79
	}
80
}
81

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

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

            
269
	serde_json::to_value(&config).expect("Could not build genesis config.")
270
}
271

            
272
/// Generate a chain spec for use with the development service.
273
pub fn development() -> serde_json::Value {
274
	testnet_genesis(
275
		// Treasury Council members: Baltathar, Charleth and Dorothy
276
		vec![
277
			AccountId::from(sp_core::hex2array!(
278
				"3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"
279
			)),
280
			AccountId::from(sp_core::hex2array!(
281
				"798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc"
282
			)),
283
			AccountId::from(sp_core::hex2array!(
284
				"773539d4Ac0e786233D90A233654ccEE26a613D9"
285
			)),
286
		],
287
		// Open Tech committee members: Alith and Baltathar
288
		vec![
289
			AccountId::from(sp_core::hex2array!(
290
				"f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
291
			)),
292
			AccountId::from(sp_core::hex2array!(
293
				"3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"
294
			)),
295
		],
296
		// Collator Candidate: Alice -> Alith
297
		vec![(
298
			AccountId::from(sp_core::hex2array!(
299
				"f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
300
			)),
301
			NimbusId::from(Sr25519Keyring::Alice.public()),
302
			100_000 * MOVR,
303
		)],
304
		// Delegations
305
		vec![],
306
		vec![
307
			AccountId::from(sp_core::hex2array!(
308
				"f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
309
			)),
310
			AccountId::from(sp_core::hex2array!(
311
				"3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"
312
			)),
313
			AccountId::from(sp_core::hex2array!(
314
				"798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc"
315
			)),
316
			AccountId::from(sp_core::hex2array!(
317
				"773539d4Ac0e786233D90A233654ccEE26a613D9"
318
			)),
319
		],
320
		Default::default(), // para_id
321
		1281,               //ChainId
322
	)
323
}
324

            
325
/// Provides the JSON representation of predefined genesis config for given `id`.
326
pub fn get_preset(id: &PresetId) -> Option<Vec<u8>> {
327
	let patch = match id.as_str() {
328
		sp_genesis_builder::DEV_RUNTIME_PRESET => development(),
329
		_ => return None,
330
	};
331
	Some(
332
		serde_json::to_string(&patch)
333
			.expect("serialization to json is expected to work. qed.")
334
			.into_bytes(),
335
	)
336
}
337

            
338
/// List of supported presets.
339
pub fn preset_names() -> Vec<PresetId> {
340
	vec![PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET)]
341
}