1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! Clap parser utilities

use std::{path::PathBuf, sync::Arc};

use alloy_genesis::Genesis;
use reth_chainspec::ChainSpec;
#[cfg(not(feature = "optimism"))]
use reth_chainspec::{DEV, HOLESKY, MAINNET, SEPOLIA};
use reth_cli::chainspec::ChainSpecParser;
use reth_fs_util as fs;
#[cfg(feature = "optimism")]
use reth_optimism_chainspec::{BASE_MAINNET, BASE_SEPOLIA, OP_DEV, OP_MAINNET, OP_SEPOLIA};

#[cfg(feature = "optimism")]
/// Chains supported by op-reth. First value should be used as the default.
pub const SUPPORTED_CHAINS: &[&str] =
    &["optimism", "optimism-sepolia", "base", "base-sepolia", "dev"];
#[cfg(not(feature = "optimism"))]
/// Chains supported by reth. First value should be used as the default.
pub const SUPPORTED_CHAINS: &[&str] = &["mainnet", "sepolia", "holesky", "dev"];

/// Clap value parser for [`ChainSpec`]s.
///
/// The value parser matches either a known chain, the path
/// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct.
#[cfg(not(feature = "optimism"))]
pub fn chain_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error> {
    Ok(match s {
        "mainnet" => MAINNET.clone(),
        "sepolia" => SEPOLIA.clone(),
        "holesky" => HOLESKY.clone(),
        "dev" => DEV.clone(),
        _ => Arc::new(parse_custom_chain_spec(s)?),
    })
}

/// Clap value parser for [`OpChainSpec`](reth_optimism_chainspec::OpChainSpec)s.
///
/// The value parser matches either a known chain, the path
/// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct.
#[cfg(feature = "optimism")]
pub fn chain_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error> {
    Ok(Arc::new(match s {
        "optimism" => OP_MAINNET.inner.clone(),
        "optimism_sepolia" | "optimism-sepolia" => OP_SEPOLIA.inner.clone(),
        "base" => BASE_MAINNET.inner.clone(),
        "base_sepolia" | "base-sepolia" => BASE_SEPOLIA.inner.clone(),
        "dev" => OP_DEV.inner.clone(),
        _ => parse_custom_chain_spec(s)?,
    }))
}

/// Parses a custom [`ChainSpec`].
pub fn parse_custom_chain_spec(s: &str) -> eyre::Result<ChainSpec, eyre::Error> {
    // try to read json from path first
    let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) {
        Ok(raw) => raw,
        Err(io_err) => {
            // valid json may start with "\n", but must contain "{"
            if s.contains('{') {
                s.to_string()
            } else {
                return Err(io_err.into()) // assume invalid path
            }
        }
    };

    // both serialized Genesis and ChainSpec structs supported
    let genesis: Genesis = serde_json::from_str(&raw)?;

    Ok(genesis.into())
}

/// Default chain specification parser.
#[derive(Debug, Clone, Default)]
pub struct DefaultChainSpecParser;

impl ChainSpecParser for DefaultChainSpecParser {
    type ChainSpec = ChainSpec;

    const SUPPORTED_CHAINS: &'static [&'static str] = SUPPORTED_CHAINS;

    fn parse(s: &str) -> eyre::Result<Arc<ChainSpec>> {
        chain_value_parser(s)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_known_chain_spec() {
        for chain in SUPPORTED_CHAINS {
            chain_value_parser(chain).unwrap();
        }
    }
}