Skip to main content

cast/cmd/
access_list.rs

1use crate::{
2    Cast,
3    tx::{CastTxBuilder, SenderKind},
4};
5use alloy_ens::NameOrAddress;
6use alloy_network::{Ethereum, Network};
7use alloy_rpc_types::BlockId;
8use clap::Parser;
9use eyre::Result;
10use foundry_cli::{
11    opts::{RpcOpts, TransactionOpts},
12    utils::LoadConfig,
13};
14use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder};
15use foundry_wallets::WalletOpts;
16use std::str::FromStr;
17use tempo_alloy::TempoNetwork;
18
19/// CLI arguments for `cast access-list`.
20#[derive(Debug, Parser)]
21pub struct AccessListArgs {
22    /// The destination of the transaction.
23    #[arg(
24        value_name = "TO",
25        value_parser = NameOrAddress::from_str
26    )]
27    to: Option<NameOrAddress>,
28
29    /// The signature of the function to call.
30    #[arg(value_name = "SIG")]
31    sig: Option<String>,
32
33    /// The arguments of the function to call.
34    #[arg(value_name = "ARGS", allow_negative_numbers = true)]
35    args: Vec<String>,
36
37    /// Raw hex-encoded data for the transaction. Used instead of \[SIG\] and \[ARGS\].
38    #[arg(
39        long,
40        conflicts_with_all = &["sig", "args"]
41    )]
42    data: Option<String>,
43
44    /// The block height to query at.
45    ///
46    /// Can also be the tags earliest, finalized, safe, latest, or pending.
47    #[arg(long, short = 'B')]
48    block: Option<BlockId>,
49
50    #[command(flatten)]
51    tx: TransactionOpts,
52
53    #[command(flatten)]
54    rpc: RpcOpts,
55
56    #[command(flatten)]
57    wallet: WalletOpts,
58}
59
60impl AccessListArgs {
61    pub async fn run(self) -> Result<()> {
62        if self.tx.tempo.is_tempo() {
63            self.run_with_network::<TempoNetwork>().await
64        } else {
65            self.run_with_network::<Ethereum>().await
66        }
67    }
68
69    pub async fn run_with_network<N: Network + Unpin>(self) -> Result<()>
70    where
71        N::TransactionRequest: FoundryTransactionBuilder<N>,
72    {
73        let Self { to, mut sig, args, data, tx, rpc, wallet, block } = self;
74
75        if let Some(data) = data {
76            sig = Some(data);
77        }
78
79        let config = rpc.load_config()?;
80        let provider = ProviderBuilder::<N>::from_config(&config)?.build()?;
81        let sender = SenderKind::from_wallet_opts(wallet).await?;
82
83        let (tx, _) = CastTxBuilder::new(&provider, tx, &config)
84            .await?
85            .with_to(to)
86            .await?
87            .with_code_sig_and_args(None, sig, args)
88            .await?
89            .raw()
90            .build(sender)
91            .await?;
92
93        let access_list: String = Cast::new(&provider).access_list(&tx, block).await?;
94
95        sh_println!("{access_list}")?;
96
97        Ok(())
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use alloy_primitives::hex;
105    use clap::error::ErrorKind;
106
107    #[test]
108    fn can_parse_access_list_data() {
109        let data = hex::encode("hello");
110        let args = AccessListArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
111        assert_eq!(args.data, Some(data));
112
113        let data = hex::encode_prefixed("hello");
114        let args = AccessListArgs::parse_from(["foundry-cli", "--data", data.as_str()]);
115        assert_eq!(args.data, Some(data));
116    }
117
118    #[test]
119    fn data_conflicts_with_sig_and_args() {
120        let err = AccessListArgs::try_parse_from([
121            "foundry-cli",
122            "0x0000000000000000000000000000000000000001",
123            "transfer(address,uint256)",
124            "0x0000000000000000000000000000000000000002",
125            "1",
126            "--data",
127            "0x1234",
128        ])
129        .unwrap_err();
130
131        assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
132    }
133}