moonbeam_service/chain_spec/
moonbeam.rs

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//! Moonbeam 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
22#[cfg(test)]
23use crate::chain_spec::{derive_bip44_pairs_from_mnemonic, get_account_id_from_pair};
24use crate::chain_spec::{generate_accounts, get_from_seed, Extensions};
25use crate::HostFunctions;
26use cumulus_primitives_core::ParaId;
27use hex_literal::hex;
28use moonbeam_runtime::{
29	currency::{GLMR, SUPPLY_FACTOR},
30	genesis_config_preset::testnet_genesis,
31	AccountId, WASM_BINARY,
32};
33use nimbus_primitives::NimbusId;
34use sc_service::ChainType;
35#[cfg(test)]
36use sp_core::ecdsa;
37
38/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
39pub type ChainSpec = sc_service::GenericChainSpec<Extensions, HostFunctions>;
40
41/// Generate a chain spec for use with the development service.
42pub fn development_chain_spec(mnemonic: Option<String>, num_accounts: Option<u32>) -> ChainSpec {
43	// Default mnemonic if none was provided
44	let parent_mnemonic = mnemonic.unwrap_or_else(|| {
45		"bottom drive obey lake curtain smoke basket hold race lonely fit walk".to_string()
46	});
47	let mut accounts = generate_accounts(parent_mnemonic, num_accounts.unwrap_or(10));
48	// We add Gerald here
49	accounts.push(AccountId::from(hex!(
50		"6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b"
51	)));
52
53	ChainSpec::builder(
54		WASM_BINARY.expect("WASM binary was not build, please build it!"),
55		Extensions {
56			relay_chain: "dev-service".into(),
57			para_id: Default::default(),
58		},
59	)
60	.with_name("Moonbeam Development Testnet")
61	.with_id("moonbeam_dev")
62	.with_chain_type(ChainType::Development)
63	.with_properties(
64		serde_json::from_str(
65			"{\"tokenDecimals\": 18, \"tokenSymbol\": \"GLMR\", \"SS58Prefix\": 1284}",
66		)
67		.expect("Provided valid json map"),
68	)
69	.with_genesis_config(testnet_genesis(
70		// Treasury Council members: Baltathar, Charleth and Dorothy
71		vec![accounts[1], accounts[2], accounts[3]],
72		// Open Tech committee members: Alith and Baltathar
73		vec![accounts[0], accounts[1]],
74		// Collator Candidate: Alice -> Alith
75		vec![(
76			accounts[0],
77			get_from_seed::<NimbusId>("Alice"),
78			20_000 * GLMR * SUPPLY_FACTOR,
79		)],
80		// Delegations
81		vec![],
82		accounts.clone(),
83		Default::default(), // para_id
84		1281,               //ChainId
85	))
86	.build()
87}
88
89/// Generate a default spec for the parachain service. Use this as a starting point when launching
90/// a custom chain.
91pub fn get_chain_spec(para_id: ParaId) -> ChainSpec {
92	ChainSpec::builder(
93		WASM_BINARY.expect("WASM binary was not build, please build it!"),
94		Extensions {
95			relay_chain: "polkadot-local".into(),
96			para_id: para_id.into(),
97		},
98	)
99	// TODO Apps depends on this string to determine whether the chain is an ethereum compat
100	// or not. We should decide the proper strings, and update Apps accordingly.
101	// Or maybe Apps can be smart enough to say if the string contains "moonbeam" at all...
102	.with_name("Moonbeam Local Testnet")
103	.with_id("moonbeam_local")
104	.with_chain_type(ChainType::Local)
105	.with_properties(
106		serde_json::from_str(
107			"{\"tokenDecimals\": 18, \"tokenSymbol\": \"GLMR\", \"SS58Prefix\": 1284}",
108		)
109		.expect("Provided valid json map"),
110	)
111	.with_genesis_config(testnet_genesis(
112		// Treasury Council members: Baltathar, Charleth and Dorothy
113		vec![
114			AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")),
115			AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")),
116			AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")),
117		],
118		// Open Tech committee members: Alith and Baltathar
119		vec![
120			AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")),
121			AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")),
122		],
123		// Collator Candidates
124		vec![
125			// Alice -> Alith
126			(
127				AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")),
128				get_from_seed::<NimbusId>("Alice"),
129				20_000 * GLMR * SUPPLY_FACTOR,
130			),
131			// Bob -> Baltathar
132			(
133				AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")),
134				get_from_seed::<NimbusId>("Bob"),
135				20_000 * GLMR * SUPPLY_FACTOR,
136			),
137		],
138		// Delegations
139		vec![],
140		// Endowed: Alith, Baltathar, Charleth and Dorothy
141		vec![
142			AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")),
143			AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")),
144			AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")),
145			AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")),
146		],
147		para_id,
148		1280, //ChainId
149	))
150	.build()
151}
152
153#[cfg(test)]
154mod tests {
155	use super::*;
156	#[test]
157	fn test_derived_pairs_1() {
158		let mnemonic =
159			"bottom drive obey lake curtain smoke basket hold race lonely fit walk".to_string();
160		let accounts = 10;
161		let pairs = derive_bip44_pairs_from_mnemonic::<ecdsa::Public>(&mnemonic, accounts);
162		let first_account = get_account_id_from_pair(pairs.first().unwrap().clone()).unwrap();
163		let last_account = get_account_id_from_pair(pairs.last().unwrap().clone()).unwrap();
164
165		let expected_first_account =
166			AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"));
167		let expected_last_account =
168			AccountId::from(hex!("2898FE7a42Be376C8BC7AF536A940F7Fd5aDd423"));
169		assert_eq!(first_account, expected_first_account);
170		assert_eq!(last_account, expected_last_account);
171		assert_eq!(pairs.len(), 10);
172	}
173	#[test]
174	fn test_derived_pairs_2() {
175		let mnemonic =
176			"slab nerve salon plastic filter inherit valve ozone crash thumb quality whale"
177				.to_string();
178		let accounts = 20;
179		let pairs = derive_bip44_pairs_from_mnemonic::<ecdsa::Public>(&mnemonic, accounts);
180		let first_account = get_account_id_from_pair(pairs.first().unwrap().clone()).unwrap();
181		let last_account = get_account_id_from_pair(pairs.last().unwrap().clone()).unwrap();
182
183		let expected_first_account =
184			AccountId::from(hex!("1e56ca71b596f2b784a27a2fdffef053dbdeff83"));
185		let expected_last_account =
186			AccountId::from(hex!("4148202BF0c0Ad7697Cff87EbB83340C80c947f8"));
187		assert_eq!(first_account, expected_first_account);
188		assert_eq!(last_account, expected_last_account);
189		assert_eq!(pairs.len(), 20);
190	}
191}