1use alloy_rpc_types_beacon::payload::BeaconBlockData;
4use clap::Parser;
5use eyre::{Result, eyre};
6use foundry_common::{fs, sh_print};
7
8#[derive(Parser)]
11pub struct B2EPayloadArgs {
12 #[arg(
15 value_name = "INPUT",
16 help = "File path to JSON file or raw JSON string containing the beacon block"
17 )]
18 pub input: String,
19}
20
21impl B2EPayloadArgs {
22 pub async fn run(self) -> Result<()> {
23 let json = read_input(self.input)?;
24 let beacon_block_data: BeaconBlockData = serde_json::from_str(&json)
25 .map_err(|e| eyre!("Failed to parse beacon block JSON: {}", e))?;
26 let output = serde_json::to_string(&beacon_block_data.execution_payload())
27 .map_err(|e| eyre!("Failed to serialize execution payload: {}", e))?;
28 sh_print!("{}", output)?;
29 Ok(())
30 }
31}
32
33fn read_input(input: String) -> Result<String> {
35 if serde_json::from_str::<serde_json::Value>(&input).is_ok() {
36 return Ok(input);
37 }
38 fs::read_to_string(&input).map_err(|e| eyre!("Failed to read JSON file '{input}': {e}"))
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn read_input_prefers_raw_json_over_paths() {
47 let json = r#"{"execution_payload": {"block_hash": "0x123"}}"#;
48 assert_eq!(read_input(json.to_string()).unwrap(), json);
49 let json = r#"[{"block": "data"}]"#;
50 assert_eq!(read_input(json.to_string()).unwrap(), json);
51
52 let file = tempfile::NamedTempFile::new().unwrap();
53 std::fs::write(file.path(), json).unwrap();
54 assert_eq!(read_input(file.path().to_string_lossy().into_owned()).unwrap(), json);
55
56 let err = read_input("not-json-{".to_string()).unwrap_err().to_string();
57 assert!(err.starts_with("Failed to read JSON file 'not-json-{'"), "{err}");
58 }
59}