mirror of
https://github.com/pezkuwichain/pezkuwi-runtime-templates.git
synced 2026-04-22 03:17:56 +00:00
* Added a possibility to predeploy contracts to the genesis block * Added some default contracts to predeploy.
This commit is contained in:
Generated
+2
@@ -7941,6 +7941,7 @@ dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-benchmarking-cli",
|
||||
"futures",
|
||||
"hex",
|
||||
"hex-literal",
|
||||
"jsonrpsee",
|
||||
"log",
|
||||
@@ -7967,6 +7968,7 @@ dependencies = [
|
||||
"sc-transaction-pool",
|
||||
"sc-transaction-pool-api",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sp-api",
|
||||
"sp-block-builder",
|
||||
|
||||
@@ -13,6 +13,7 @@ repository = "https://github.com/OpenZeppelin/polkadot-runtime-template"
|
||||
clap = { version = "4.5.3", features = [ "derive" ] }
|
||||
color-print = "0.3.4"
|
||||
futures = "0.3.28"
|
||||
hex = "0.4.3"
|
||||
hex-literal = "0.4.1"
|
||||
jsonrpsee = { version = "0.22", features = [ "server" ] }
|
||||
log = { version = "0.4.20", default-features = false }
|
||||
@@ -22,6 +23,7 @@ parity-scale-codec = { version = "3.0.0", default-features = false, features = [
|
||||
] }
|
||||
scale-info = { version = "2.10.0", default-features = false }
|
||||
serde = { version = "1.0.188", default-features = false }
|
||||
serde_derive = { version = "1.0.188", default-features = false }
|
||||
serde_json = "1.0.108"
|
||||
smallvec = "1.11.0"
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"address": "0x81ead4918134AE386dbd04346216E20AB8F822C4",
|
||||
"filename": "Entrypoint.json"
|
||||
}
|
||||
]
|
||||
@@ -12,11 +12,13 @@ version = "0.1.2"
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
hex-literal = { workspace = true }
|
||||
jsonrpsee = { workspace = true, features = [ "server" ] }
|
||||
log = { workspace = true }
|
||||
parity-scale-codec = { workspace = true }
|
||||
serde = { workspace = true, features = [ "derive" ] }
|
||||
serde_derive = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
# Local
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::collections::BTreeMap;
|
||||
use cumulus_primitives_core::ParaId;
|
||||
use fp_evm::GenesisAccount;
|
||||
use hex_literal::hex;
|
||||
use log::error;
|
||||
use parachain_template_runtime::{
|
||||
constants::currency::EXISTENTIAL_DEPOSIT, AccountId, AuraId,
|
||||
OpenZeppelinPrecompiles as Precompiles, Runtime, Signature,
|
||||
@@ -13,6 +14,8 @@ use serde::{Deserialize, Serialize};
|
||||
use sp_core::{ecdsa, Pair, Public, H160};
|
||||
use sp_runtime::traits::{IdentifyAccount, Verify};
|
||||
|
||||
use crate::contracts::{parse_contracts, ContractsPath};
|
||||
|
||||
/// Specialized `ChainSpec` for the normal parachain runtime.
|
||||
pub type ChainSpec =
|
||||
sc_service::GenericChainSpec<parachain_template_runtime::RuntimeGenesisConfig, Extensions>;
|
||||
@@ -70,7 +73,7 @@ pub fn template_session_keys(keys: AuraId) -> parachain_template_runtime::Sessio
|
||||
parachain_template_runtime::SessionKeys { aura: keys }
|
||||
}
|
||||
|
||||
pub fn development_config() -> ChainSpec {
|
||||
pub fn development_config(contracts_path: ContractsPath) -> ChainSpec {
|
||||
// Give your base currency a unit name and decimal places
|
||||
let mut properties = sc_chain_spec::Properties::new();
|
||||
properties.insert("tokenSymbol".into(), "UNIT".into());
|
||||
@@ -118,11 +121,12 @@ pub fn development_config() -> ChainSpec {
|
||||
],
|
||||
get_account_id_from_seed::<ecdsa::Public>("Alice"),
|
||||
1000.into(),
|
||||
contracts_path,
|
||||
))
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn local_testnet_config() -> ChainSpec {
|
||||
pub fn local_testnet_config(contracts_path: ContractsPath) -> ChainSpec {
|
||||
// Give your base currency a unit name and decimal places
|
||||
let mut properties = sc_chain_spec::Properties::new();
|
||||
properties.insert("tokenSymbol".into(), "UNIT".into());
|
||||
@@ -167,6 +171,7 @@ pub fn local_testnet_config() -> ChainSpec {
|
||||
],
|
||||
get_account_id_from_seed::<ecdsa::Public>("Alice"),
|
||||
1000.into(),
|
||||
contracts_path,
|
||||
))
|
||||
.with_protocol_id("template-local")
|
||||
.with_properties(properties)
|
||||
@@ -178,7 +183,41 @@ fn testnet_genesis(
|
||||
endowed_accounts: Vec<AccountId>,
|
||||
root: AccountId,
|
||||
id: ParaId,
|
||||
contracts_path: ContractsPath,
|
||||
) -> serde_json::Value {
|
||||
let contracts = parse_contracts(contracts_path)
|
||||
.map_err(|e| error!("Error while parsing contracts: {e:?}"))
|
||||
.unwrap_or_default();
|
||||
let precompiles = Precompiles::<Runtime>::used_addresses()
|
||||
.map(|addr| {
|
||||
(
|
||||
addr,
|
||||
GenesisAccount {
|
||||
nonce: Default::default(),
|
||||
balance: Default::default(),
|
||||
storage: Default::default(),
|
||||
// bytecode to revert without returning data
|
||||
// (PUSH1 0x00 PUSH1 0x00 REVERT)
|
||||
code: vec![0x60, 0x00, 0x60, 0x00, 0xFD],
|
||||
},
|
||||
)
|
||||
})
|
||||
.into_iter();
|
||||
let accounts: BTreeMap<H160, GenesisAccount> = contracts
|
||||
.into_iter()
|
||||
.map(|(address, contract)| {
|
||||
(
|
||||
address,
|
||||
GenesisAccount {
|
||||
code: contract.bytecode(),
|
||||
nonce: Default::default(),
|
||||
balance: Default::default(),
|
||||
storage: Default::default(),
|
||||
},
|
||||
)
|
||||
})
|
||||
.chain(precompiles)
|
||||
.collect();
|
||||
serde_json::json!({
|
||||
"balances": {
|
||||
"balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::<Vec<_>>(),
|
||||
@@ -207,22 +246,7 @@ fn testnet_genesis(
|
||||
"chainId": 9999
|
||||
},
|
||||
"evm": {
|
||||
"accounts": Precompiles::<Runtime>::used_addresses()
|
||||
.map(|addr| {
|
||||
(
|
||||
addr,
|
||||
GenesisAccount {
|
||||
nonce: Default::default(),
|
||||
balance: Default::default(),
|
||||
storage: Default::default(),
|
||||
// bytecode to revert without returning data
|
||||
// (PUSH1 0x00 PUSH1 0x00 REVERT)
|
||||
code: vec![0x60, 0x00, 0x60, 0x00, 0xFD],
|
||||
},
|
||||
)
|
||||
})
|
||||
.into_iter()
|
||||
.collect::<BTreeMap<H160, GenesisAccount>>(),
|
||||
"accounts": accounts
|
||||
},
|
||||
"polkadotXcm": {
|
||||
"safeXcmVersion": Some(SAFE_XCM_VERSION),
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::eth::EthConfiguration;
|
||||
use sc_cli::{ChainSpec, Result};
|
||||
use sc_network::config::NetworkConfiguration;
|
||||
|
||||
use crate::{contracts::ContractsPath, eth::EthConfiguration};
|
||||
|
||||
/// Sub-commands supported by the collator.
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum Subcommand {
|
||||
/// Build a chain specification.
|
||||
BuildSpec(sc_cli::BuildSpecCmd),
|
||||
BuildSpec(ExtendedBuildSpecCmd),
|
||||
|
||||
/// Validate blocks.
|
||||
CheckBlock(sc_cli::CheckBlockCmd),
|
||||
@@ -47,6 +50,48 @@ pub enum Subcommand {
|
||||
TryRuntime,
|
||||
}
|
||||
|
||||
impl Subcommand {
|
||||
pub fn contract_directory(&self) -> ContractsPath {
|
||||
match self {
|
||||
Self::BuildSpec(cmd) =>
|
||||
match (cmd.no_predeployed_contracts, cmd.predeployed_contracts.clone()) {
|
||||
(true, _) => ContractsPath::None,
|
||||
(false, None) => ContractsPath::Default,
|
||||
(false, Some(path)) => ContractsPath::Some(path),
|
||||
},
|
||||
_ => ContractsPath::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, clap::Parser)]
|
||||
pub struct ExtendedBuildSpecCmd {
|
||||
#[clap(flatten)]
|
||||
pub cmd: sc_cli::BuildSpecCmd,
|
||||
|
||||
/// Path to a directory that contains precompiled contracts.
|
||||
/// A directory should contain file `contracts.json`.
|
||||
/// It should contain a JSON array of objects with "address" and "filename" files in it. See the example in `evm-template/contracts/contracts.json`
|
||||
/// If you don't specify any contract, only Entrypoint contract will be deployed at the address `0x81ead4918134AE386dbd04346216E20AB8F822C4`
|
||||
#[arg(long, default_value = None)]
|
||||
pub predeployed_contracts: Option<String>,
|
||||
|
||||
/// Set this to true to if you do not want any contracts to be deployed
|
||||
#[arg(long, default_value = "false")]
|
||||
pub no_predeployed_contracts: bool,
|
||||
}
|
||||
|
||||
impl ExtendedBuildSpecCmd {
|
||||
/// Run the build-spec command
|
||||
pub fn run(
|
||||
&self,
|
||||
spec: Box<dyn ChainSpec>,
|
||||
network_config: NetworkConfiguration,
|
||||
) -> Result<()> {
|
||||
self.cmd.run(spec, network_config)
|
||||
}
|
||||
}
|
||||
|
||||
const AFTER_HELP_EXAMPLE: &str = color_print::cstr!(
|
||||
r#"<bold><underline>Examples:</></>
|
||||
<bold>parachain-template-node build-spec --disable-default-bootnode > plain-parachain-chainspec.json</>
|
||||
|
||||
@@ -14,15 +14,19 @@ use sp_runtime::traits::AccountIdConversion;
|
||||
|
||||
use crate::{
|
||||
chain_spec,
|
||||
cli::{Cli, RelayChainCli, Subcommand},
|
||||
cli::{Cli, ExtendedBuildSpecCmd, RelayChainCli, Subcommand},
|
||||
contracts::ContractsPath,
|
||||
service::new_partial,
|
||||
};
|
||||
|
||||
fn load_spec(id: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
|
||||
fn load_spec(
|
||||
id: &str,
|
||||
contracts_path: ContractsPath,
|
||||
) -> std::result::Result<Box<dyn ChainSpec>, String> {
|
||||
Ok(match id {
|
||||
"dev" => Box::new(chain_spec::development_config()),
|
||||
"template-rococo" => Box::new(chain_spec::local_testnet_config()),
|
||||
"" | "local" => Box::new(chain_spec::local_testnet_config()),
|
||||
"dev" => Box::new(chain_spec::development_config(contracts_path)),
|
||||
"template-rococo" => Box::new(chain_spec::local_testnet_config(contracts_path)),
|
||||
"" | "local" => Box::new(chain_spec::local_testnet_config(contracts_path)),
|
||||
path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?),
|
||||
})
|
||||
}
|
||||
@@ -58,7 +62,10 @@ impl SubstrateCli for Cli {
|
||||
}
|
||||
|
||||
fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
|
||||
load_spec(id)
|
||||
load_spec(
|
||||
id,
|
||||
self.subcommand.as_ref().map(Subcommand::contract_directory).unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,3 +396,13 @@ impl CliConfiguration<Self> for RelayChainCli {
|
||||
self.base.base.node_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl CliConfiguration for ExtendedBuildSpecCmd {
|
||||
fn shared_params(&self) -> &SharedParams {
|
||||
self.cmd.shared_params()
|
||||
}
|
||||
|
||||
fn node_key_params(&self) -> Option<&sc_cli::NodeKeyParams> {
|
||||
self.cmd.node_key_params()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/// This part was easy to implement and may be useful in future.
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AbiItem {
|
||||
Error(Error),
|
||||
Function(Function),
|
||||
Constructor,
|
||||
Event(Event),
|
||||
Receive(Receive),
|
||||
Default(Function),
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Error {
|
||||
name: String,
|
||||
inputs: Vec<Input>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Receive {
|
||||
#[serde(rename = "stateMutability")]
|
||||
state_mutability: StateMutability,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Event {
|
||||
name: String,
|
||||
inputs: Vec<Input>,
|
||||
anonymous: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Function {
|
||||
name: String,
|
||||
inputs: Vec<Input>,
|
||||
outputs: Vec<Input>,
|
||||
#[serde(rename = "stateMutability")]
|
||||
state_mutability: StateMutability,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Input {
|
||||
name: String,
|
||||
r#type: String,
|
||||
components: Option<Vec<Input>>,
|
||||
indexed: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum StateMutability {
|
||||
Pure,
|
||||
View,
|
||||
Nonpayable,
|
||||
Payable,
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,15 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{abi::AbiItem, deserialize_bytecode};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ForgeContract {
|
||||
pub abi: Vec<AbiItem>,
|
||||
pub bytecode: ForgeBytecode,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ForgeBytecode {
|
||||
#[serde(deserialize_with = "deserialize_bytecode")]
|
||||
pub object: Vec<u8>,
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{abi::AbiItem, deserialize_bytecode};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct HardhatContract {
|
||||
pub abi: Vec<AbiItem>,
|
||||
#[serde(deserialize_with = "deserialize_bytecode")]
|
||||
pub bytecode: Vec<u8>,
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::File,
|
||||
io::{self, BufReader},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use serde::{de, Deserialize};
|
||||
use sp_core::H160;
|
||||
|
||||
use self::{
|
||||
defaults::{entrypoint_address, entrypoint_contract},
|
||||
forge::ForgeContract,
|
||||
hardhat::HardhatContract,
|
||||
};
|
||||
|
||||
mod abi;
|
||||
mod defaults;
|
||||
mod forge;
|
||||
mod hardhat;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ContractMetadata {
|
||||
pub address: H160,
|
||||
pub filename: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Contract {
|
||||
Hardhat(HardhatContract),
|
||||
Forge(ForgeContract),
|
||||
}
|
||||
|
||||
impl Contract {
|
||||
pub fn bytecode(self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Hardhat(c) => c.bytecode,
|
||||
Self::Forge(c) => c.bytecode.object,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ContractParsingError {
|
||||
MetadataReadError(io::Error),
|
||||
MetadataParseError(serde_json::Error),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum ContractsPath {
|
||||
None,
|
||||
#[default]
|
||||
Default,
|
||||
Some(String),
|
||||
}
|
||||
|
||||
pub fn parse_contracts(
|
||||
path: ContractsPath,
|
||||
) -> Result<HashMap<H160, Contract>, ContractParsingError> {
|
||||
let mut res = HashMap::new();
|
||||
let path = match path {
|
||||
ContractsPath::None => return Ok(res),
|
||||
ContractsPath::Default => {
|
||||
res.insert(entrypoint_address(), entrypoint_contract());
|
||||
return Ok(res);
|
||||
}
|
||||
ContractsPath::Some(path) => path,
|
||||
};
|
||||
let path = Path::new(&path);
|
||||
let metadata_path = path.join(Path::new("contracts.json"));
|
||||
let metadata_file =
|
||||
File::open(metadata_path.as_path()).map_err(ContractParsingError::MetadataReadError)?;
|
||||
let metadata_reader = BufReader::new(metadata_file);
|
||||
let metadatas: Vec<ContractMetadata> = serde_json::from_reader(metadata_reader)
|
||||
.map_err(ContractParsingError::MetadataParseError)?;
|
||||
for metadata in metadatas {
|
||||
let ContractMetadata { filename, address } = metadata;
|
||||
let contract_path = path.join(Path::new(&filename));
|
||||
let Ok(contract_file) = File::open(contract_path.as_path()).map_err(|e| {
|
||||
println!("File {filename} can't be opened, skipping: {e:?}");
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
let contract_reader = BufReader::new(contract_file);
|
||||
let Ok(contract) = serde_json::from_reader(contract_reader).map_err(|e| {
|
||||
println!("File {filename} can't be parsed, skipping: {e:?}");
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
res.insert(address, contract);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn deserialize_bytecode<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
let s: &str = de::Deserialize::deserialize(deserializer)?;
|
||||
hex::decode(&s[2..]).map_err(|e| de::Error::custom(e.to_string()))
|
||||
}
|
||||
@@ -7,6 +7,7 @@ mod chain_spec;
|
||||
mod service;
|
||||
mod cli;
|
||||
mod command;
|
||||
mod contracts;
|
||||
mod eth;
|
||||
mod rpc;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user