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

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

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

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

            
255
	serde_json::to_value(&config).expect("Could not build genesis config.")
256
}
257

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

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

            
325
/// List of supported presets.
326
pub fn preset_names() -> Vec<PresetId> {
327
	vec![PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET)]
328
}