Skip to main content

cast/cmd/
da_estimate.rs

1//! Estimates the data availability size of a block for opstack.
2
3use alloy_consensus::BlockHeader;
4use alloy_network::{BlockResponse, Ethereum, Network, eip2718::Encodable2718};
5use alloy_provider::Provider;
6use alloy_rpc_types::BlockId;
7use clap::Parser;
8use eyre::Result;
9use foundry_cli::{opts::RpcOpts, utils::LoadConfig};
10use foundry_common::provider::ProviderBuilder;
11use foundry_config::Config;
12use foundry_evm_networks::NetworkVariant;
13
14#[cfg(feature = "base")]
15use base_common_network::Base;
16
17#[cfg(feature = "optimism")]
18use op_alloy_network::Optimism;
19
20/// CLI arguments for `cast da-estimate`.
21#[derive(Debug, Parser)]
22pub struct DAEstimateArgs {
23    /// The block to estimate the data availability size for.
24    pub block: BlockId,
25    #[command(flatten)]
26    pub rpc: RpcOpts,
27    /// Specify the Network for correct encoding.
28    #[arg(long, short, num_args = 1, value_name = "NETWORK")]
29    network: Option<NetworkVariant>,
30}
31
32impl DAEstimateArgs {
33    pub async fn run(self) -> Result<()> {
34        let Self { block, rpc, network } = self;
35        let config = rpc.load_config()?;
36        let network = match network {
37            Some(n) => n,
38            None => super::resolve_transaction_network(&config, false).await?,
39        };
40        match network {
41            #[cfg(feature = "base")]
42            NetworkVariant::Base => da_estimate::<Base>(&config, block).await,
43            #[cfg(feature = "optimism")]
44            NetworkVariant::Optimism => da_estimate::<Optimism>(&config, block).await,
45            NetworkVariant::Ethereum => da_estimate::<Ethereum>(&config, block).await,
46            other => eyre::bail!(
47                "DA estimation is not supported for {other:?}: EIP-4844 blob transactions are not available on this network"
48            ),
49        }
50    }
51}
52
53async fn da_estimate<N: Network>(config: &Config, block_id: BlockId) -> Result<()> {
54    let provider = ProviderBuilder::<N>::from_config(config)?.build()?;
55    let block =
56        provider.get_block(block_id).full().await?.ok_or_else(|| eyre::eyre!("Block not found"))?;
57
58    let block_number = block.header().number();
59    let tx_count = block.transactions().len();
60    let da_estimate = block
61        .transactions()
62        .txns()
63        .map(|tx| op_alloy_flz::tx_estimated_size_fjord(&tx.as_ref().encoded_2718()))
64        .sum::<u64>();
65    sh_status!(
66        "Estimated data availability size for block {block_number} with {tx_count} transactions:"
67    )?;
68    sh_println!("{da_estimate}")?;
69    Ok(())
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[tokio::test]
77    async fn rejects_networks_without_blob_transactions() {
78        let args = DAEstimateArgs {
79            block: BlockId::latest(),
80            rpc: RpcOpts::default(),
81            network: Some(NetworkVariant::Tempo),
82        };
83        let err = args.run().await.unwrap_err().to_string();
84        assert!(err.contains("Tempo"), "{err}");
85        assert!(err.contains("EIP-4844"), "{err}");
86    }
87}