Skip to main content

cast/cmd/
auth.rs

1use super::confirm_continue;
2use crate::tx::{CastTxBuilder, InputState, SenderKind, validate_authorizations};
3use alloy_network::{Network, TransactionBuilder};
4use alloy_provider::Provider;
5use eyre::Result;
6use foundry_cli::{
7    opts::CliAuthorizationList,
8    utils::{ResolvedLane, maybe_print_resolved_lane},
9};
10use foundry_common::{FoundryTransactionBuilder, shell};
11use foundry_wallets::TempoAccountsWallet;
12
13/// Validates the authorization sender and confirms that the user intends to disclose an EIP-7702
14/// authorization to an RPC endpoint.
15///
16/// Returns `false` when the user declines and the command should exit without sending the
17/// authorization.
18pub(super) fn confirm_auth_rpc_disclosure<N: Network, P, S>(
19    builder: &CastTxBuilder<N, P, S>,
20    sender: &SenderKind<'_>,
21    force: bool,
22) -> Result<bool> {
23    builder.validate_auth(sender)?;
24    confirm_auth_rpc_disclosure_after_validation(force)
25}
26
27/// Validates and confirms disclosure before an execution network is resolved from the RPC.
28pub(super) fn confirm_auth_rpc_disclosure_before_network_resolution(
29    authorizations: &[CliAuthorizationList],
30    sender: &SenderKind<'_>,
31    force: bool,
32) -> Result<bool> {
33    validate_authorizations(authorizations, sender)?;
34    confirm_auth_rpc_disclosure_after_validation(force)
35}
36
37fn confirm_auth_rpc_disclosure_after_validation(force: bool) -> Result<bool> {
38    if force {
39        return Ok(true);
40    }
41    if shell::is_quiet() {
42        eyre::bail!(
43            "EIP-7702 authorization disclosure requires confirmation; pass `--force` to continue with `--quiet`"
44        );
45    }
46
47    sh_warn!(
48        "This command will send a signed EIP-7702 authorization to the RPC endpoint. The authorization can be submitted on-chain by anyone once its nonce is valid."
49    )?;
50    confirm_continue()
51}
52
53/// Confirms the authorization disclosure, builds the transaction for `sender` and prints the
54/// resolved lane. `rpc_signs` marks modes where the RPC signs the transaction, which discloses
55/// every authorization regardless of how the request is filled.
56///
57/// Returns `None` when the user declined.
58pub(super) async fn confirm_and_build<'a, N: Network, P: Provider<N>>(
59    builder: CastTxBuilder<N, P, InputState>,
60    sender: impl Into<SenderKind<'a>>,
61    force: bool,
62    lane: Option<&ResolvedLane>,
63    rpc_signs: bool,
64) -> Result<Option<N::TransactionRequest>>
65where
66    N::TransactionRequest: FoundryTransactionBuilder<N>,
67{
68    let sender = sender.into();
69    let discloses =
70        if rpc_signs { builder.has_auth() } else { builder.will_disclose_auth_during_build() };
71    if discloses && !confirm_auth_rpc_disclosure(&builder, &sender, force)? {
72        return Ok(None);
73    }
74    let (tx, _) = builder.build(sender).await?;
75    maybe_print_resolved_lane(lane, tx.nonce().unwrap_or_default())?;
76    Ok(Some(tx))
77}
78
79/// [`confirm_and_build`] for a transaction signed by a Tempo access key; also returns the
80/// prepared wallet.
81pub(super) async fn confirm_and_build_with_tempo_wallet<N: Network, P: Provider<N>>(
82    builder: CastTxBuilder<N, P, InputState>,
83    wallet: &TempoAccountsWallet,
84    force: bool,
85    lane: Option<&ResolvedLane>,
86) -> Result<Option<(N::TransactionRequest, TempoAccountsWallet)>>
87where
88    N::TransactionRequest: FoundryTransactionBuilder<N>,
89{
90    if builder.will_disclose_auth_during_build()
91        && !confirm_auth_rpc_disclosure(&builder, &wallet.account().into(), force)?
92    {
93        return Ok(None);
94    }
95    let (tx, _, prepared) = builder.build_with_tempo_wallet(wallet).await?;
96    maybe_print_resolved_lane(lane, tx.nonce().unwrap_or_default())?;
97    Ok(Some((tx, prepared)))
98}