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