#![allow(clippy::derive_partial_eq_without_eq)]
use altair_runtime::constants::currency::{AIR, MILLI_AIR};
use cfg_primitives::{
currency_decimals, parachains, AccountId, AuraId, Balance, BlockNumber, CFG, MILLI_CFG,
SAFE_XCM_VERSION,
};
use cfg_types::{
domain_address::DomainAddress,
fee_keys::FeeKey,
tokens::{usdc, AssetMetadata, CrossChainTransferability, CurrencyId, CustomMetadata},
};
use cfg_utils::vec_to_fixed_array;
use cumulus_primitives_core::ParaId;
use hex_literal::hex;
use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup};
use sc_service::{ChainType, Properties};
use serde::{Deserialize, Serialize};
use sp_core::{sr25519, Encode, Pair, Public};
use sp_runtime::{
traits::{IdentifyAccount, Verify},
FixedPointNumber,
};
use staging_xcm::{
latest::{Location, NetworkId},
prelude::{AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, PalletInstance, Parachain},
};
pub type AltairChainSpec =
sc_service::GenericChainSpec<altair_runtime::RuntimeGenesisConfig, Extensions>;
pub type CentrifugeChainSpec =
sc_service::GenericChainSpec<centrifuge_runtime::RuntimeGenesisConfig, Extensions>;
pub type DevelopmentChainSpec =
sc_service::GenericChainSpec<development_runtime::RuntimeGenesisConfig, Extensions>;
use cfg_types::fixed_point::Rate;
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{seed}"), None)
.expect("static values are valid; qed")
.public()
}
#[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension,
)]
#[serde(deny_unknown_fields)]
pub struct Extensions {
pub relay_chain: String,
pub para_id: u32,
pub first_evm_block: BlockNumber,
}
impl Extensions {
pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Extensions> {
sc_chain_spec::get_extension(chain_spec.extensions())
}
}
fn development_extensions(para_id: u32) -> Extensions {
Extensions {
para_id,
relay_chain: "rococo-local".into(),
first_evm_block: 1,
}
}
pub fn get_altair_session_keys(keys: AuraId) -> altair_runtime::SessionKeys {
altair_runtime::SessionKeys {
aura: keys.clone(),
block_rewards: keys,
}
}
pub fn get_centrifuge_session_keys(keys: AuraId) -> centrifuge_runtime::SessionKeys {
centrifuge_runtime::SessionKeys {
aura: keys.clone(),
block_rewards: keys,
}
}
pub fn get_development_session_keys(keys: AuraId) -> development_runtime::SessionKeys {
development_runtime::SessionKeys {
aura: keys.clone(),
block_rewards: keys,
}
}
type AccountPublic = <cfg_primitives::Signature as Verify>::Signer;
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
where
AccountPublic: From<<TPublic::Pair as Pair>::Public>,
{
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}
pub fn centrifuge_config() -> CentrifugeChainSpec {
CentrifugeChainSpec::from_json_bytes(
&include_bytes!("../res/genesis/centrifuge-genesis-spec-raw.json")[..],
)
.unwrap()
}
pub fn centrifuge_local(para_id: ParaId) -> CentrifugeChainSpec {
let mut properties = Properties::new();
properties.insert("tokenSymbol".into(), "DCFG".into());
properties.insert("tokenDecimals".into(), currency_decimals::NATIVE.into());
CentrifugeChainSpec::builder(
centrifuge_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"),
development_extensions(para_id.into()),
)
.with_name("Centrifuge Local")
.with_id("centrifuge_local")
.with_chain_type(ChainType::Local)
.with_genesis_config_patch(centrifuge_genesis(
vec![(
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_from_seed::<AuraId>("Alice"),
)],
endowed_accounts(),
endowed_evm_accounts(),
Some(100000000 * CFG),
para_id,
council_members_bootstrap(),
))
.with_properties(properties)
.build()
}
pub fn catalyst_config() -> CentrifugeChainSpec {
CentrifugeChainSpec::from_json_bytes(&include_bytes!("../res/catalyst-spec-raw.json")[..])
.unwrap()
}
pub fn altair_config() -> AltairChainSpec {
AltairChainSpec::from_json_bytes(
&include_bytes!("../res/genesis/altair-genesis-spec-raw.json")[..],
)
.unwrap()
}
pub fn demo_config() -> DevelopmentChainSpec {
DevelopmentChainSpec::from_json_bytes(&include_bytes!("../res/demo-spec-raw.json")[..]).unwrap()
}
pub fn altair_local(para_id: ParaId) -> AltairChainSpec {
let mut properties = Properties::new();
properties.insert("tokenSymbol".into(), "DAIR".into());
properties.insert("tokenDecimals".into(), currency_decimals::NATIVE.into());
AltairChainSpec::builder(
altair_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"),
development_extensions(para_id.into()),
)
.with_name("Altair Local")
.with_id("altair_local")
.with_chain_type(ChainType::Local)
.with_genesis_config_patch(altair_genesis(
vec![(
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_from_seed::<AuraId>("Alice"),
)],
endowed_accounts(),
endowed_evm_accounts(),
Some(100000000 * AIR),
para_id,
council_members_bootstrap(),
))
.with_properties(properties)
.build()
}
pub fn development(para_id: ParaId) -> DevelopmentChainSpec {
let mut properties = Properties::new();
properties.insert("tokenSymbol".into(), "DEVEL".into());
properties.insert("tokenDecimals".into(), currency_decimals::NATIVE.into());
DevelopmentChainSpec::builder(
development_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"),
development_extensions(para_id.into()),
)
.with_name("Dev Live")
.with_id("devel_live")
.with_chain_type(ChainType::Live)
.with_genesis_config_patch(development_genesis(
get_account_id_from_seed::<sr25519::Public>("Alice"),
vec![(
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_from_seed::<AuraId>("Alice"),
)],
endowed_accounts(),
endowed_evm_accounts(),
Some(10000000 * CFG),
para_id,
))
.with_properties(properties)
.build()
}
fn endowed_accounts() -> Vec<AccountId> {
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
]
}
fn endowed_evm_accounts() -> Vec<([u8; 20], Option<u64>)> {
vec![(
hex!["7F429e2e38BDeFa7a2E797e3BEB374a3955746a4"],
None,
)]
}
fn council_members_bootstrap() -> Vec<AccountId> {
endowed_accounts().into_iter().take(4).collect::<Vec<_>>()
}
fn centrifuge_genesis(
initial_authorities: Vec<(AccountId, AuraId)>,
mut endowed_accounts: Vec<AccountId>,
endowed_evm_accounts: Vec<([u8; 20], Option<u64>)>,
total_issuance: Option<Balance>,
id: ParaId,
council_members: Vec<AccountId>,
) -> serde_json::Value {
let chain_id: u32 = id.into();
endowed_accounts.extend(endowed_evm_accounts.into_iter().map(|(addr, id)| {
let chain_id = id.unwrap_or(chain_id.into());
DomainAddress::Evm(chain_id, addr.into()).account()
}));
let num_endowed_accounts = endowed_accounts.len();
let balances = match total_issuance {
Some(total_issuance) => {
let balance_per_endowed = total_issuance
.checked_div(num_endowed_accounts as Balance)
.unwrap_or(0 as Balance);
endowed_accounts
.iter()
.cloned()
.map(|k| (k, balance_per_endowed))
.collect::<Vec<_>>()
}
None => vec![],
};
serde_json::json!({
"balances": { "balances": balances },
"council": {
"members": council_members,
},
"fees": {
"initialFees": vec![(
FeeKey::AnchorsCommit,
2_365_296_803_653u128,
)],
},
"parachainInfo": {
"parachainId": id,
},
"collatorSelection": {
"invulnerables": initial_authorities
.iter()
.cloned()
.map(|(acc, _)| acc)
.collect::<Vec<_>>(),
"candidacyBond": 1 * CFG,
},
"session": {
"keys": initial_authorities
.iter()
.cloned()
.map(|(acc, aura)| {
(
acc.clone(), acc, get_centrifuge_session_keys(aura), )
})
.collect::<Vec<_>>(),
},
"bridge": {
"chains": vec![0],
"resources": vec![
(
hex!["00000000000000000000000000000009e974040e705c10fb4de576d6cc261900"],
hex!["50616c6c65744272696467652e7472616e73666572"].to_vec(),
),
],
"relayers": vec![
Into::<AccountId>::into(hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"]),
Into::<AccountId>::into(hex!["c405224448dcd4259816b09cfedbd8df0e6796b16286ea18efa2d6343da5992e"]),
],
"threshold": 1,
},
"blockRewards": {
"collators": initial_authorities
.iter()
.cloned()
.map(|(acc, _)| acc)
.collect::<Vec<_>>(),
"collatorReward": 8_325 * MILLI_CFG,
"treasuryInflationRate": Rate::saturating_from_rational(3, 100),
},
"evmChainId": {
"chainId": Into::<u32>::into(chain_id),
},
"evm": {
"accounts": runtime_common::evm::utils::account_genesis::<centrifuge_runtime::Precompiles>(),
},
"polkadotXcm": {
"safeXcmVersion": Some(SAFE_XCM_VERSION),
},
})
}
fn altair_genesis(
initial_authorities: Vec<(AccountId, AuraId)>,
mut endowed_accounts: Vec<AccountId>,
endowed_evm_accounts: Vec<([u8; 20], Option<u64>)>,
total_issuance: Option<Balance>,
id: ParaId,
council_members: Vec<AccountId>,
) -> serde_json::Value {
let chain_id: u32 = id.into();
endowed_accounts.extend(endowed_evm_accounts.into_iter().map(|(addr, id)| {
let chain_id = id.unwrap_or(chain_id.into());
DomainAddress::Evm(chain_id, addr.into()).account()
}));
let num_endowed_accounts = endowed_accounts.len();
let balances = match total_issuance {
Some(total_issuance) => {
let balance_per_endowed = total_issuance
.checked_div(num_endowed_accounts as Balance)
.unwrap_or(0 as Balance);
endowed_accounts
.iter()
.cloned()
.map(|k| (k, balance_per_endowed))
.collect::<Vec<_>>()
}
None => vec![],
};
serde_json::json!({
"balances": { "balances": balances },
"council": {
"members": council_members.clone(),
},
"fees": {
"initialFees": vec![(
FeeKey::AnchorsCommit,
2_365_296_803_653u128,
)],
},
"parachainInfo": {
"parachainId": id,
},
"collatorSelection": {
"invulnerables": initial_authorities
.iter()
.cloned()
.map(|(acc, _)| acc)
.collect::<Vec<_>>(),
"candidacyBond": 1 * AIR,
},
"blockRewards": {
"collators": initial_authorities
.iter()
.cloned()
.map(|(acc, _)| acc)
.collect::<Vec<_>>(),
"collatorReward": 98_630 * MILLI_AIR,
"treasuryInflationRate": Rate::saturating_from_rational(3, 100),
},
"session": {
"keys": initial_authorities
.iter()
.cloned()
.map(|(acc, aura)| {
(
acc.clone(), acc, get_altair_session_keys(aura), )
})
.collect::<Vec<_>>(),
},
"evmChainId": {
"chainId": Into::<u32>::into(chain_id),
},
"evm": {
"accounts": runtime_common::evm::utils::account_genesis::<altair_runtime::Precompiles>(),
},
"polkadotXcm": {
"safeXcmVersion": Some(SAFE_XCM_VERSION),
},
"technicalCommittee": {
"members": council_members
}
})
}
const DEV_USDT_CURRENCY_ID: CurrencyId = CurrencyId::ForeignAsset(1);
const DEV_AUSD_CURRENCY_ID: CurrencyId = CurrencyId::ForeignAsset(2);
fn development_genesis(
root_key: AccountId,
initial_authorities: Vec<(AccountId, AuraId)>,
mut endowed_accounts: Vec<AccountId>,
endowed_evm_accounts: Vec<([u8; 20], Option<u64>)>,
total_issuance: Option<Balance>,
id: ParaId,
) -> serde_json::Value {
let chain_id: u32 = id.into();
endowed_accounts.extend(endowed_evm_accounts.into_iter().map(|(addr, id)| {
let chain_id = id.unwrap_or(chain_id.into());
DomainAddress::Evm(chain_id, addr.into()).account()
}));
let num_endowed_accounts = endowed_accounts.len();
let (balances, token_balances) = match total_issuance {
Some(total_issuance) => {
let balance_per_endowed = total_issuance
.checked_div(num_endowed_accounts as Balance)
.unwrap_or(0 as Balance);
(
endowed_accounts
.iter()
.cloned()
.map(|x| (x, balance_per_endowed))
.collect::<Vec<_>>(),
endowed_accounts
.iter()
.cloned()
.flat_map(|x| {
vec![
(x.clone(), DEV_USDT_CURRENCY_ID, 1_000_000_000_000u128),
(x, DEV_AUSD_CURRENCY_ID, 1_000_000_000_000_000_000u128),
]
})
.collect::<Vec<_>>(),
)
}
None => (vec![], vec![]),
};
let chain_id: u32 = id.into();
serde_json::json!({
"balances": { "balances": balances },
"ormlAssetRegistry": {
"assets": asset_registry_assets(),
},
"ormlTokens": {
"balances": token_balances,
},
"fees": {
"initialFees": vec![(
FeeKey::AnchorsCommit,
2_365_296_803_653u128,
)],
},
"sudo": {
"key": Some(root_key),
},
"parachainInfo": {
"parachainId": id,
},
"collatorSelection": {
"invulnerables": initial_authorities
.iter()
.cloned()
.map(|(acc, _)| acc)
.collect::<Vec<_>>(),
"candidacyBond": 1 * CFG,
},
"session": {
"keys": initial_authorities
.iter()
.cloned()
.map(|(acc, aura)| {
(
acc.clone(), acc, get_development_session_keys(aura), )
})
.collect::<Vec<_>>(),
},
"bridge": {
"chains": vec![0],
"resources": vec![
(
hex!["00000000000000000000000000000009e974040e705c10fb4de576d6cc261900"],
hex!["50616c6c65744272696467652e7472616e73666572"].to_vec(),
),
],
"relayers": vec![
Into::<AccountId>::into(hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"]),
Into::<AccountId>::into(hex!["c405224448dcd4259816b09cfedbd8df0e6796b16286ea18efa2d6343da5992e"]),
],
"threshold": 1,
},
"blockRewards": {
"collators": initial_authorities
.iter()
.cloned()
.map(|(acc, _)| acc)
.collect::<Vec<_>>(),
"collatorReward": 8_325 * MILLI_CFG,
"treasuryInflationRate": Rate::saturating_from_rational(3, 100),
},
"evmChainId": {
"chainId": Into::<u32>::into(chain_id),
},
"evm": {
"accounts": runtime_common::evm::utils::account_genesis::<development_runtime::Precompiles>(),
},
"polkadotXcm": {
"safeXcmVersion": Some(SAFE_XCM_VERSION),
},
})
}
fn asset_registry_assets() -> Vec<(CurrencyId, Vec<u8>)> {
vec![
(
DEV_USDT_CURRENCY_ID,
AssetMetadata {
decimals: 6,
name: b"Tether USD"
.to_vec()
.try_into()
.expect("fit in the BoundedVec"),
symbol: b"USDT".to_vec().try_into().expect("fit in the BoundedVec"),
existential_deposit: 0u128,
location: Some(staging_xcm::VersionedLocation::V4(Location::new(
1,
[
Parachain(parachains::rococo::rocksmine::ID),
PalletInstance(parachains::rococo::rocksmine::usdt::PALLET_INSTANCE),
GeneralIndex(parachains::rococo::rocksmine::usdt::GENERAL_INDEX),
],
))),
additional: CustomMetadata {
mintable: false,
permissioned: false,
pool_currency: true,
transferability: CrossChainTransferability::Xcm(Default::default()),
local_representation: None,
},
}
.encode(),
),
(
DEV_AUSD_CURRENCY_ID,
AssetMetadata {
decimals: 12,
name: b"Acala USD"
.to_vec()
.try_into()
.expect("fit in the BoundedVec"),
symbol: b"AUSD".to_vec().try_into().expect("fit in the BoundedVec"),
existential_deposit: 0u128,
location: Some(staging_xcm::VersionedLocation::V4(Location::new(
1,
[
Parachain(parachains::rococo::acala::ID),
GeneralKey {
length: parachains::rococo::acala::AUSD_KEY.to_vec().len() as u8,
data: vec_to_fixed_array(parachains::rococo::acala::AUSD_KEY),
},
],
))),
additional: CustomMetadata {
mintable: false,
permissioned: false,
pool_currency: true,
transferability: CrossChainTransferability::Xcm(Default::default()),
local_representation: None,
},
}
.encode(),
),
(
usdc::CURRENCY_ID_LOCAL,
AssetMetadata {
decimals: 6,
name: b"Local USDC"
.to_vec()
.try_into()
.expect("fit in the BoundedVec"),
symbol: b"localUSDC"
.to_vec()
.try_into()
.expect("fit in the BoundedVec"),
existential_deposit: 0u128,
location: None,
additional: CustomMetadata {
mintable: false,
permissioned: false,
pool_currency: true,
transferability: CrossChainTransferability::None,
local_representation: None,
},
}
.encode(),
),
(
usdc::CURRENCY_ID_LP_ETH_GOERLI,
AssetMetadata {
decimals: usdc::DECIMALS,
name: b"LP Ethereum Wrapped USDC"
.to_vec()
.try_into()
.expect("fit in the BoundedVec"),
symbol: b"LpEthUSDC"
.to_vec()
.try_into()
.expect("fit in the BoundedVec"),
existential_deposit: usdc::EXISTENTIAL_DEPOSIT,
location: Some(staging_xcm::VersionedLocation::V4(Location::new(
0,
[
PalletInstance(development_runtime::LiquidityPoolsPalletIndex::get()),
GlobalConsensus(NetworkId::Ethereum {
chain_id: usdc::CHAIN_ID_ETH_GOERLI_TESTNET,
}),
AccountKey20 {
network: None,
key: usdc::CONTRACT_ETH_GOERLI,
},
],
))),
additional: CustomMetadata {
transferability: CrossChainTransferability::LiquidityPools,
mintable: false,
permissioned: false,
pool_currency: true,
local_representation: Some(usdc::LOCAL_ASSET_ID),
},
}
.encode(),
),
]
}