1use std::path::PathBuf;
4
5use alloy_rpc_types_beacon::payload::BeaconBlockData;
6use clap::{Parser, builder::ValueParser};
7use eyre::{Result, eyre};
8use foundry_common::{fs, sh_print};
9
10#[derive(Parser)]
13pub struct B2EPayloadArgs {
14 #[arg(value_name = "INPUT", value_parser=ValueParser::new(parse_input_source), help = "File path to JSON file or raw JSON string containing the beacon block")]
17 pub input: InputSource,
18}
19
20impl B2EPayloadArgs {
21 pub async fn run(self) -> Result<()> {
22 let beacon_block_json = match self.input {
23 InputSource::Json(json) => json,
24 InputSource::File(path) => fs::read_to_string(&path)
25 .map_err(|e| eyre!("Failed to read JSON file '{}': {}", path.display(), e))?,
26 };
27
28 let beacon_block_data: BeaconBlockData = serde_json::from_str(&beacon_block_json)
29 .map_err(|e| eyre!("Failed to parse beacon block JSON: {}", e))?;
30
31 let execution_payload = beacon_block_data.execution_payload();
32
33 let output = serde_json::to_string(&execution_payload)
35 .map_err(|e| eyre!("Failed to serialize execution payload: {}", e))?;
36 sh_print!("{}", output)?;
37
38 Ok(())
39 }
40}
41
42#[derive(Debug, Clone)]
44pub enum InputSource {
45 File(PathBuf),
47 Json(String),
49}
50
51fn parse_input_source(s: &str) -> Result<InputSource, String> {
52 if serde_json::from_str::<serde_json::Value>(s).is_ok() {
54 return Ok(InputSource::Json(s.to_string()));
55 }
56
57 Ok(InputSource::File(PathBuf::from(s)))
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_parse_input_source_json_object() {
67 let json_input = r#"{"execution_payload": {"block_hash": "0x123"}}"#;
68 let result = parse_input_source(json_input).unwrap();
69
70 match result {
71 InputSource::Json(json) => assert_eq!(json, json_input),
72 InputSource::File(_) => panic!("Expected JSON input, got File"),
73 }
74 }
75
76 #[test]
77 fn test_parse_input_source_json_array() {
78 let json_input = r#"[{"block": "data"}]"#;
79 let result = parse_input_source(json_input).unwrap();
80
81 match result {
82 InputSource::Json(json) => assert_eq!(json, json_input),
83 InputSource::File(_) => panic!("Expected JSON input, got File"),
84 }
85 }
86
87 #[test]
88 fn test_parse_input_source_file_path() {
89 let file_path =
90 "block-12225729-6ceadbf2a6adbbd64cbec33fdebbc582f25171cd30ac43f641cbe76ac7313ddf.json";
91 let result = parse_input_source(file_path).unwrap();
92
93 match result {
94 InputSource::File(path) => assert_eq!(path, PathBuf::from(file_path)),
95 InputSource::Json(_) => panic!("Expected File input, got JSON"),
96 }
97 }
98
99 #[test]
100 fn test_parse_input_source_malformed_but_not_json() {
101 let malformed = "not-json-{";
102 let result = parse_input_source(malformed).unwrap();
103
104 match result {
106 InputSource::File(path) => assert_eq!(path, PathBuf::from(malformed)),
107 InputSource::Json(_) => panic!("Expected File input, got File"),
108 }
109 }
110}