Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ca2a51d
feat: add v1 cast commande b2epayload
lean-apple Sep 12, 2025
a9de85e
feat: add v1 cast commande b2epayload
lean-apple Sep 12, 2025
14342de
chore: fmt
lean-apple Sep 12, 2025
819199d
Merge branch 'foundry-rs:master' into master
lean-apple Sep 12, 2025
65b924e
Merge branch 'master' into cast-convert-beacon-to-el-block
lean-apple Sep 12, 2025
920096b
chore: use pathbuf
lean-apple Sep 12, 2025
a5fa112
Merge branch 'master' into cast-convert-beacon-to-el-block
lean-apple Sep 12, 2025
ade3eda
fix: restore long name
lean-apple Sep 12, 2025
7a94011
chore: fmt
lean-apple Sep 12, 2025
713be9c
Merge branch 'foundry-rs:master' into master
lean-apple Sep 21, 2025
bd462ec
Merge branch 'master' into cast-convert-beacon-to-el-block
lean-apple Sep 21, 2025
f43b27b
Merge branch 'master' into cast-convert-beacon-to-el-block
lean-apple Sep 22, 2025
eb11d6b
Merge branch 'master' into cast-convert-beacon-to-el-block
lean-apple Sep 24, 2025
86e5092
Merge branch 'master' into cast-convert-beacon-to-el-block
lean-apple Sep 25, 2025
9cd99e0
chore: update lock
lean-apple Sep 25, 2025
6cf5318
refactor: use ex payload alloy helper
lean-apple Sep 25, 2025
8507478
Merge branch 'master' into cast-convert-beacon-to-el-block
lean-apple Sep 25, 2025
4185dd3
refactor: use raw input source str
lean-apple Sep 25, 2025
3b89ca9
refactor: remove right now json rpc format output
lean-apple Sep 25, 2025
35f5304
test: add test for malformated input
lean-apple Sep 25, 2025
27b308c
chore: remove unused dep
lean-apple Sep 25, 2025
fc52cb8
chore: fmt
lean-apple Sep 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ alloy-provider = { version = "1.0.36", default-features = false }
alloy-pubsub = { version = "1.0.36", default-features = false }
alloy-rpc-client = { version = "1.0.36", default-features = false }
alloy-rpc-types = { version = "1.0.36", default-features = true }
alloy-rpc-types-beacon = { version = "1.0.36", default-features = true }
alloy-serde = { version = "1.0.36", default-features = false }
alloy-signer = { version = "1.0.36", default-features = false }
alloy-signer-aws = { version = "1.0.36", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions crates/cast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ alloy-provider = { workspace = true, features = [
] }
alloy-rlp.workspace = true
alloy-rpc-types = { workspace = true, features = ["eth", "trace"] }
alloy-rpc-types-beacon.workspace = true
alloy-serde.workspace = true
alloy-signer-local = { workspace = true, features = ["mnemonic", "keystore"] }
alloy-signer.workspace = true
Expand Down
1 change: 1 addition & 0 deletions crates/cast/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> {
CastSubcommand::ConstructorArgs(cmd) => cmd.run().await?,
CastSubcommand::Artifact(cmd) => cmd.run().await?,
CastSubcommand::Bind(cmd) => cmd.run().await?,
CastSubcommand::B2EPayload(cmd) => cmd.run().await?,
CastSubcommand::PrettyCalldata { calldata, offline } => {
let calldata = stdin::unwrap_line(calldata)?;
sh_println!("{}", pretty_calldata(&calldata, offline).await?)?;
Expand Down
110 changes: 110 additions & 0 deletions crates/cast/src/cmd/b2e_payload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! Command Line handler to convert Beacon block's execution payload to Execution format.

use std::path::PathBuf;

use alloy_rpc_types_beacon::payload::BeaconBlockData;
use clap::{Parser, builder::ValueParser};
use eyre::{Result, eyre};
use foundry_common::{fs, sh_print};

/// CLI arguments for `cast b2e-payload`, convert Beacon block's execution payload to Execution
/// format.
#[derive(Parser)]
pub struct B2EPayloadArgs {
/// Input data, it can be either a file path to JSON file or raw JSON string containing the
/// beacon block
#[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")]
pub input: InputSource,
}

impl B2EPayloadArgs {
pub async fn run(self) -> Result<()> {
let beacon_block_json = match self.input {
InputSource::Json(json) => json,
InputSource::File(path) => fs::read_to_string(&path)
.map_err(|e| eyre!("Failed to read JSON file '{}': {}", path.display(), e))?,
};

let beacon_block_data: BeaconBlockData = serde_json::from_str(&beacon_block_json)
.map_err(|e| eyre!("Failed to parse beacon block JSON: {}", e))?;

let execution_payload = beacon_block_data.execution_payload();

// Output raw execution payload
let output = serde_json::to_string(&execution_payload)
.map_err(|e| eyre!("Failed to serialize execution payload: {}", e))?;
sh_print!("{}", output)?;

Ok(())
}
}

/// Represents the different input sources for beacon block data
#[derive(Debug, Clone)]
pub enum InputSource {
/// Path to a JSON file containing beacon block data
File(PathBuf),
/// Raw JSON string containing beacon block data
Json(String),
}

fn parse_input_source(s: &str) -> Result<InputSource, String> {
// Try parsing as JSON first
if serde_json::from_str::<serde_json::Value>(s).is_ok() {
return Ok(InputSource::Json(s.to_string()));
}

// Otherwise treat as file path
Ok(InputSource::File(PathBuf::from(s)))
}

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

#[test]
fn test_parse_input_source_json_object() {
let json_input = r#"{"execution_payload": {"block_hash": "0x123"}}"#;
let result = parse_input_source(json_input).unwrap();

match result {
InputSource::Json(json) => assert_eq!(json, json_input),
InputSource::File(_) => panic!("Expected JSON input, got File"),
}
}

#[test]
fn test_parse_input_source_json_array() {
let json_input = r#"[{"block": "data"}]"#;
let result = parse_input_source(json_input).unwrap();

match result {
InputSource::Json(json) => assert_eq!(json, json_input),
InputSource::File(_) => panic!("Expected JSON input, got File"),
}
}

#[test]
fn test_parse_input_source_file_path() {
let file_path =
"block-12225729-6ceadbf2a6adbbd64cbec33fdebbc582f25171cd30ac43f641cbe76ac7313ddf.json";
let result = parse_input_source(file_path).unwrap();

match result {
InputSource::File(path) => assert_eq!(path, PathBuf::from(file_path)),
InputSource::Json(_) => panic!("Expected File input, got JSON"),
}
}

#[test]
fn test_parse_input_source_malformed_but_not_json() {
let malformed = "not-json-{";
let result = parse_input_source(malformed).unwrap();

// Should be treated as file path since it's not valid JSON
match result {
InputSource::File(path) => assert_eq!(path, PathBuf::from(malformed)),
InputSource::Json(_) => panic!("Expected File input, got File"),
}
}
}
1 change: 1 addition & 0 deletions crates/cast/src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

pub mod access_list;
pub mod artifact;
pub mod b2e_payload;
pub mod bind;
pub mod call;
pub mod constructor_args;
Expand Down
15 changes: 10 additions & 5 deletions crates/cast/src/opts.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::cmd::{
access_list::AccessListArgs, artifact::ArtifactArgs, bind::BindArgs, call::CallArgs,
constructor_args::ConstructorArgsArgs, create2::Create2Args, creation_code::CreationCodeArgs,
da_estimate::DAEstimateArgs, estimate::EstimateArgs, find_block::FindBlockArgs,
interface::InterfaceArgs, logs::LogsArgs, mktx::MakeTxArgs, rpc::RpcArgs, run::RunArgs,
send::SendTxArgs, storage::StorageArgs, txpool::TxPoolSubcommands, wallet::WalletSubcommands,
access_list::AccessListArgs, artifact::ArtifactArgs, b2e_payload::B2EPayloadArgs,
bind::BindArgs, call::CallArgs, constructor_args::ConstructorArgsArgs, create2::Create2Args,
creation_code::CreationCodeArgs, da_estimate::DAEstimateArgs, estimate::EstimateArgs,
find_block::FindBlockArgs, interface::InterfaceArgs, logs::LogsArgs, mktx::MakeTxArgs,
rpc::RpcArgs, run::RunArgs, send::SendTxArgs, storage::StorageArgs, txpool::TxPoolSubcommands,
wallet::WalletSubcommands,
};
use alloy_ens::NameOrAddress;
use alloy_primitives::{Address, B256, Selector, U256};
Expand Down Expand Up @@ -1054,6 +1055,10 @@ pub enum CastSubcommand {
#[command(visible_alias = "bi")]
Bind(BindArgs),

/// Convert Beacon payload to execution payload.
#[command(visible_alias = "b2e")]
B2EPayload(B2EPayloadArgs),

/// Get the selector for a function.
#[command(visible_alias = "si")]
Sig {
Expand Down
Loading