Skip to main content

foundry_common/transactions/
broadcast.rs

1use alloy_consensus::Transaction;
2use alloy_eips::eip7702::SignedAuthorization;
3use alloy_network::{Network, TransactionBuilder};
4use alloy_primitives::{Address, Bytes, U256};
5use foundry_common_fmt::UIfmt;
6use serde::{Deserialize, Serialize};
7
8use super::FoundryTransactionBuilder;
9
10/// Used for broadcasting transactions
11/// A transaction can either be a `TransactionRequest` waiting to be signed
12/// or a `TxEnvelope`, already signed
13#[derive(Clone, Debug, Serialize, Deserialize)]
14#[serde(untagged)]
15pub enum TransactionMaybeSigned<N: Network> {
16    Signed {
17        #[serde(flatten)]
18        tx: N::TxEnvelope,
19        from: Address,
20    },
21    Unsigned(N::TransactionRequest),
22}
23
24impl<N: Network> TransactionMaybeSigned<N> {
25    /// Creates a new (unsigned) transaction for broadcast
26    pub fn new(tx: N::TransactionRequest) -> Self {
27        Self::Unsigned(tx)
28    }
29
30    pub fn is_unsigned(&self) -> bool {
31        matches!(self, Self::Unsigned(_))
32    }
33
34    pub fn as_unsigned_mut(&mut self) -> Option<&mut N::TransactionRequest> {
35        match self {
36            Self::Unsigned(tx) => Some(tx),
37            _ => None,
38        }
39    }
40
41    pub fn from(&self) -> Option<Address> {
42        match self {
43            Self::Signed { from, .. } => Some(*from),
44            Self::Unsigned(tx) => tx.from(),
45        }
46    }
47
48    pub fn input(&self) -> Option<&Bytes> {
49        match self {
50            Self::Signed { tx, .. } => Some(tx.input()),
51            Self::Unsigned(tx) => tx.input(),
52        }
53    }
54
55    pub fn to(&self) -> Option<Address> {
56        match self {
57            Self::Signed { tx, .. } => tx.to(),
58            Self::Unsigned(tx) => tx.to(),
59        }
60    }
61
62    pub fn value(&self) -> Option<U256> {
63        match self {
64            Self::Signed { tx, .. } => Some(tx.value()),
65            Self::Unsigned(tx) => tx.value(),
66        }
67    }
68
69    pub fn gas(&self) -> Option<u128> {
70        match self {
71            Self::Signed { tx, .. } => Some(tx.gas_limit() as u128),
72            Self::Unsigned(tx) => tx.gas_limit().map(|g| g as u128),
73        }
74    }
75
76    pub fn nonce(&self) -> Option<u64> {
77        match self {
78            Self::Signed { tx, .. } => Some(tx.nonce()),
79            Self::Unsigned(tx) => tx.nonce(),
80        }
81    }
82
83    pub fn authorization_list(&self) -> Option<Vec<SignedAuthorization>>
84    where
85        N::TransactionRequest: FoundryTransactionBuilder<N>,
86    {
87        match self {
88            Self::Signed { tx, .. } => tx.authorization_list().map(|auths| auths.to_vec()),
89            Self::Unsigned(tx) => tx.authorization_list().cloned(),
90        }
91        .filter(|auths| !auths.is_empty())
92    }
93}
94
95impl<N: Network> UIfmt for TransactionMaybeSigned<N>
96where
97    N::TxEnvelope: UIfmt,
98    N::TransactionRequest: FoundryTransactionBuilder<N>,
99{
100    fn pretty(&self) -> String {
101        match self {
102            Self::Signed { tx, .. } => tx.pretty(),
103            Self::Unsigned(tx) => format!(
104                "
105accessList           {}
106chainId              {}
107gasLimit             {}
108gasPrice             {}
109input                {}
110maxFeePerBlobGas     {}
111maxFeePerGas         {}
112maxPriorityFeePerGas {}
113nonce                {}
114to                   {}
115type                 {}
116value                {}",
117                tx.access_list()
118                    .as_ref()
119                    .map(|a| a.iter().collect::<Vec<_>>())
120                    .unwrap_or_default()
121                    .pretty(),
122                tx.chain_id().pretty(),
123                tx.gas_limit().unwrap_or_default(),
124                tx.gas_price().pretty(),
125                tx.input().pretty(),
126                tx.max_fee_per_blob_gas().pretty(),
127                tx.max_fee_per_gas().pretty(),
128                tx.max_priority_fee_per_gas().pretty(),
129                tx.nonce().pretty(),
130                tx.to().pretty(),
131                tx.output_tx_type(),
132                tx.value().pretty(),
133            ),
134        }
135    }
136}