Skip to main content

cast/cmd/safe/
mod.rs

1use alloy_network::Ethereum;
2use alloy_provider::Provider;
3use clap::{Parser, ValueEnum};
4use eyre::Result;
5use foundry_cli::{opts::RpcOpts, utils::LoadConfig};
6use foundry_common::provider::{ProviderBuilder, RetryProvider};
7
8mod contracts;
9mod delegates;
10mod deploy;
11mod execute;
12mod proposal;
13mod service;
14mod signing;
15mod simulate;
16mod transaction;
17
18/// Safe transaction operations.
19#[derive(Debug, Parser)]
20pub enum SafeSubcommand {
21    /// Deploy a Safe account.
22    ///
23    /// Examples:
24    /// - cast safe create $OWNER --threshold 1 --rpc-url $RPC --ledger
25    /// - cast safe create $OWNER_1 $OWNER_2 $OWNER_3 --threshold 2 --rpc-url $RPC --account
26    ///   deployer
27    #[command(verbatim_doc_comment)]
28    Create(deploy::CreateArgs),
29
30    /// Register a transaction-service delegate for a Safe owner.
31    AddDelegate(delegates::AddDelegateArgs),
32
33    /// List transaction-service delegates registered for a Safe.
34    ListDelegates(delegates::ListDelegatesArgs),
35
36    /// Remove a transaction-service delegate for a Safe owner.
37    RemoveDelegate(delegates::RemoveDelegateArgs),
38
39    /// Create, sign, and submit a Safe transaction proposal.
40    Propose(proposal::ProposeArgs),
41
42    /// Sign and submit a confirmation for a proposed Safe transaction.
43    Sign(proposal::SignArgs),
44
45    /// Simulate a proposed Safe transaction without requiring owner signatures.
46    ///
47    /// This simulates the inner CALL or DELEGATECALL in the Safe's context. It does not validate
48    /// the Safe nonce, owner signatures, threshold, or guard hooks. Reimbursed transactions
49    /// (`gasPrice > 0`) are rejected because SimulateTxAccessor does not enforce `safeTxGas`.
50    #[command(verbatim_doc_comment)]
51    Simulate(simulate::SimulateArgs),
52
53    /// Execute a confirmed Safe transaction onchain.
54    Execute(execute::ExecuteArgs),
55}
56
57#[derive(Clone, Copy, Debug, Default, ValueEnum)]
58#[repr(u8)]
59pub enum SafeOperation {
60    #[default]
61    Call = 0,
62    DelegateCall = 1,
63}
64
65impl SafeSubcommand {
66    pub async fn run(self) -> Result<()> {
67        match self {
68            Self::Create(args) => args.run().await,
69            Self::AddDelegate(args) => args.run().await,
70            Self::ListDelegates(args) => args.run().await,
71            Self::RemoveDelegate(args) => args.run().await,
72            Self::Propose(args) => args.run().await,
73            Self::Sign(args) => args.run().await,
74            Self::Simulate(args) => args.run().await,
75            Self::Execute(args) => args.run().await,
76        }
77    }
78}
79
80/// Builds the read-only provider for `rpc` and returns it together with its chain ID.
81async fn rpc_provider(rpc: &RpcOpts) -> Result<(RetryProvider<Ethereum>, u64)> {
82    let provider = ProviderBuilder::<Ethereum>::from_config(&rpc.load_config()?)?.build()?;
83    let chain_id = provider.get_chain_id().await?;
84    Ok((provider, chain_id))
85}