Skip to content

Commit

Permalink
#1718 - adds option to write anvil config to json file (#1854)
Browse files Browse the repository at this point in the history
* Adds config_out option to anvil

* Write config data to file with config_out option provided

* format

* Remove extra info from config out

because the whole point is to make it easy to
interact with the config programatically

* Address comments - make code more idiomatic
  • Loading branch information
robertabbott authored Jun 7, 2022
1 parent 07b3520 commit 75d366d
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 84 deletions.
8 changes: 8 additions & 0 deletions anvil/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ pub struct NodeArgs {
)]
pub block_time: Option<u64>,

#[clap(
long,
help = "Writes output of `anvil` as json to user-specified file",
value_name = "OUT_FILE"
)]
pub config_out: Option<String>,

#[clap(
long,
visible_alias = "no-mine",
Expand Down Expand Up @@ -139,6 +146,7 @@ impl NodeArgs {
.with_server_config(self.server_config)
.with_host(self.host)
.set_silent(self.silent)
.set_config_out(self.config_out)
.with_chain_id(self.evm_opts.env.chain_id.unwrap_or(CHAIN_ID))
.with_transaction_order(self.order)
}
Expand Down
263 changes: 179 additions & 84 deletions anvil/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ use foundry_evm::{
revm::{BlockEnv, CfgEnv, SpecId, TxEnv},
};
use parking_lot::RwLock;
use std::{net::IpAddr, path::PathBuf, str::FromStr, sync::Arc, time::Duration};
use serde_json::{json, to_writer, Value};
use std::{
collections::HashMap, fmt::Write as FmtWrite, fs::File, net::IpAddr, path::PathBuf,
str::FromStr, sync::Arc, time::Duration,
};
use yansi::Paint;

/// Default port the rpc will open
Expand Down Expand Up @@ -106,6 +110,159 @@ pub struct NodeConfig {
pub host: Option<IpAddr>,
/// How transactions are sorted in the mempool
pub transaction_order: TransactionOrder,
/// Filename to write anvil output as json
pub config_out: Option<String>,
}

impl NodeConfig {
fn as_string(&self, fork: Option<&ClientFork>) -> String {
let mut config_string: String = "".to_owned();
let _ = write!(config_string, "\n{}", Paint::green(BANNER));
let _ = write!(config_string, "\n {}", VERSION_MESSAGE);
let _ = write!(
config_string,
"\n {}",
Paint::green("https://github.com/foundry-rs/foundry")
);

let _ = write!(
config_string,
r#"
Available Accounts
==================
"#
);
let balance = format_ether(self.genesis_balance);
for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
let _ = write!(config_string, "\n({}) {:?} ({} ETH)", idx, wallet.address(), balance);
}

let _ = write!(
config_string,
r#"
Private Keys
==================
"#
);

for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
let hex = hex::encode(wallet.signer().to_bytes());
let _ = write!(config_string, "\n({}) 0x{}", idx, hex);
}

if let Some(ref gen) = self.account_generator {
let _ = write!(
config_string,
r#"
Wallet
==================
Mnemonic: {}
Derivation path: {}
"#,
gen.phrase,
gen.get_derivation_path()
);
}

let _ = write!(
config_string,
r#"
Base Fee
==================
{}
"#,
Paint::green(format!("\n{}", self.base_fee))
);
let _ = write!(
config_string,
r#"
Gas Price
==================
{}
"#,
Paint::green(format!("\n{}", self.gas_price))
);

let _ = write!(
config_string,
r#"
Gas Limit
==================
{}
"#,
Paint::green(format!("\n{}", self.gas_limit))
);

if let Some(fork) = fork {
let _ = write!(
config_string,
r#"
Fork
==================
Endpoint: {}
Block number: {}
Block hash: {:?}
Chain ID: {}
"#,
fork.eth_rpc_url(),
fork.block_number(),
fork.block_hash(),
fork.chain_id()
);
}

config_string
}

fn as_json(&self, fork: Option<&ClientFork>) -> Value {
let mut wallet_description = HashMap::new();
let mut available_accounts = Vec::with_capacity(self.genesis_accounts.len());
let mut private_keys = Vec::with_capacity(self.genesis_accounts.len());

for wallet in &self.genesis_accounts {
available_accounts.push(format!("{:?}", wallet.address()));
private_keys.push(format!("0x{}", hex::encode(wallet.signer().to_bytes())));
}

if let Some(ref gen) = self.account_generator {
let phrase = gen.get_phrase().to_string();
let derivation_path = gen.get_derivation_path().to_string();

wallet_description.insert("derivation_path".to_string(), derivation_path);
wallet_description.insert("mnemonic".to_string(), phrase);
};

let config = if let Some(fork) = fork {
json!({
"available_accounts": available_accounts,
"private_keys": private_keys,
"endpoint": fork.eth_rpc_url(),
"block_number": fork.block_number(),
"block_hash": fork.block_hash(),
"chain_id": fork.chain_id(),
"wallet": wallet_description,
"base_fee": format!("{}", self.base_fee),
"gas_price": format!("{}", self.gas_price),
"gas_limit": format!("{}", self.gas_limit),
})
} else {
json!({
"available_accounts": available_accounts,
"private_keys": private_keys,
"wallet": wallet_description,
"base_fee": format!("{}", self.base_fee),
"gas_price": format!("{}", self.gas_price),
"gas_limit": format!("{}", self.gas_limit),
})
};

config
}
}

// === impl NodeConfig ===
Expand Down Expand Up @@ -146,6 +303,7 @@ impl Default for NodeConfig {
server_config: Default::default(),
host: None,
transaction_order: Default::default(),
config_out: None,
}
}
}
Expand Down Expand Up @@ -272,6 +430,13 @@ impl NodeConfig {
self
}

/// Sets the file path to write the Anvil node's config info to.
#[must_use]
pub fn set_config_out(mut self, config_out: Option<String>) -> Self {
self.config_out = config_out;
self
}

/// Makes the node silent to not emit anything on stdout
#[must_use]
pub fn no_storage_caching(self) -> Self {
Expand Down Expand Up @@ -326,93 +491,19 @@ impl NodeConfig {

/// Prints the config info
pub fn print(&self, fork: Option<&ClientFork>) {
if self.config_out.is_some() {
let config_out = self.config_out.as_deref().unwrap();
to_writer(
&File::create(config_out).expect("Unable to create anvil config description file"),
&self.as_json(fork),
)
.expect("Failed writing json");
}
if self.silent {
return
}
println!("{}", Paint::green(BANNER));
println!(" {}", VERSION_MESSAGE);
println!(" {}", Paint::green("https://github.com/foundry-rs/foundry"));

print!(
r#"
Available Accounts
==================
"#
);
let balance = format_ether(self.genesis_balance);
for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
println!("({}) {:?} ({} ETH)", idx, wallet.address(), balance);
}

print!(
r#"
Private Keys
==================
"#
);

for (idx, wallet) in self.genesis_accounts.iter().enumerate() {
let hex = hex::encode(wallet.signer().to_bytes());
println!("({}) 0x{}", idx, hex);
}

if let Some(ref gen) = self.account_generator {
print!(
r#"
Wallet
==================
Mnemonic: {}
Derivation path: {}
"#,
gen.phrase,
gen.get_derivation_path()
);
}

print!(
r#"
Base Fee
==================
{}
"#,
Paint::green(format!("{}", self.base_fee))
);
print!(
r#"
Gas Price
==================
{}
"#,
Paint::green(format!("{}", self.gas_price))
);

print!(
r#"
Gas Limit
==================
{}
"#,
Paint::green(format!("{}", self.gas_limit))
);

if let Some(fork) = fork {
print!(
r#"
Fork
==================
Endpoint: {}
Block number: {}
Block hash: {:?}
Chain ID: {}
"#,
fork.eth_rpc_url(),
fork.block_number(),
fork.block_hash(),
fork.chain_id()
);
}

println!();
println!("{}", self.as_string(fork))
}

/// Returns the path where the cache file should be stored
Expand Down Expand Up @@ -620,6 +711,10 @@ impl AccountGenerator {
self
}

fn get_phrase(&self) -> &str {
&self.phrase
}

#[must_use]
pub fn chain_id(mut self, chain_id: impl Into<u64>) -> Self {
self.chain_id = chain_id.into();
Expand Down

0 comments on commit 75d366d

Please sign in to comment.