alloy_network/transaction/
builder.rs

1use super::signer::NetworkWallet;
2use crate::Network;
3use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256};
4use alloy_rpc_types_eth::{AccessList, TransactionInputKind};
5use alloy_sol_types::SolCall;
6use futures_utils_wasm::impl_future;
7
8pub use alloy_network_primitives::{TransactionBuilder4844, TransactionBuilder7702};
9
10/// Result type for transaction builders
11pub type BuildResult<T, N> = Result<T, UnbuiltTransactionError<N>>;
12
13/// An unbuilt transaction, along with some error.
14#[derive(Debug, thiserror::Error)]
15#[error("Failed to build transaction: {error}")]
16pub struct UnbuiltTransactionError<N: Network> {
17    /// The original request that failed to build.
18    pub request: N::TransactionRequest,
19    /// The error that occurred.
20    #[source]
21    pub error: TransactionBuilderError<N>,
22}
23
24/// Error type for transaction builders.
25#[derive(Debug, thiserror::Error)]
26pub enum TransactionBuilderError<N: Network> {
27    /// Invalid transaction request
28    #[error("{0} transaction can't be built due to missing keys: {1:?}")]
29    InvalidTransactionRequest(N::TxType, Vec<&'static str>),
30
31    /// Signer cannot produce signature type required for transaction.
32    #[error("Signer cannot produce signature type required for transaction")]
33    UnsupportedSignatureType,
34
35    /// Signer error.
36    #[error(transparent)]
37    Signer(#[from] alloy_signer::Error),
38
39    /// A custom error.
40    #[error("{0}")]
41    Custom(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
42}
43
44impl<N: Network> TransactionBuilderError<N> {
45    /// Instantiate a custom error.
46    pub fn custom<E>(e: E) -> Self
47    where
48        E: std::error::Error + Send + Sync + 'static,
49    {
50        Self::Custom(Box::new(e))
51    }
52
53    /// Convert the error into an unbuilt transaction error.
54    pub const fn into_unbuilt(self, request: N::TransactionRequest) -> UnbuiltTransactionError<N> {
55        UnbuiltTransactionError { request, error: self }
56    }
57}
58
59/// A Transaction builder for a network.
60///
61/// Transaction builders are primarily used to construct typed transactions that can be signed with
62/// [`TransactionBuilder::build`], or unsigned typed transactions with
63/// [`TransactionBuilder::build_unsigned`].
64///
65/// Transaction builders should be able to construct all available transaction types on a given
66/// network.
67#[doc(alias = "TxBuilder")]
68pub trait TransactionBuilder<N: Network>: Default + Sized + Send + Sync + 'static {
69    /// Get the chain ID for the transaction.
70    fn chain_id(&self) -> Option<ChainId>;
71
72    /// Set the chain ID for the transaction.
73    fn set_chain_id(&mut self, chain_id: ChainId);
74
75    /// Builder-pattern method for setting the chain ID.
76    fn with_chain_id(mut self, chain_id: ChainId) -> Self {
77        self.set_chain_id(chain_id);
78        self
79    }
80
81    /// Get the nonce for the transaction.
82    fn nonce(&self) -> Option<u64>;
83
84    /// Set the nonce for the transaction.
85    fn set_nonce(&mut self, nonce: u64);
86
87    /// Takes the nonce out of the transaction, clearing it.
88    fn take_nonce(&mut self) -> Option<u64>;
89
90    /// Builder-pattern method for setting the nonce.
91    fn with_nonce(mut self, nonce: u64) -> Self {
92        self.set_nonce(nonce);
93        self
94    }
95
96    /// Takes the nonce out of the transaction, clearing it.
97    fn without_nonce(mut self) -> Self {
98        self.take_nonce();
99        self
100    }
101
102    /// Get the input data for the transaction.
103    fn input(&self) -> Option<&Bytes>;
104
105    /// Set the input data for the transaction.
106    fn set_input<T: Into<Bytes>>(&mut self, input: T);
107
108    /// Builder-pattern method for setting the input data.
109    fn with_input<T: Into<Bytes>>(mut self, input: T) -> Self {
110        self.set_input(input);
111        self
112    }
113
114    /// Set the input data for the transaction, respecting the input kind
115    fn set_input_kind<T: Into<Bytes>>(&mut self, input: T, _: TransactionInputKind) {
116        // forward all to input by default
117        self.set_input(input);
118    }
119
120    /// Builder-pattern method for setting the input data, respecting the input kind
121    fn with_input_kind<T: Into<Bytes>>(mut self, input: T, kind: TransactionInputKind) -> Self {
122        self.set_input_kind(input, kind);
123        self
124    }
125
126    /// Get the sender for the transaction.
127    fn from(&self) -> Option<Address>;
128
129    /// Set the sender for the transaction.
130    fn set_from(&mut self, from: Address);
131
132    /// Builder-pattern method for setting the sender.
133    fn with_from(mut self, from: Address) -> Self {
134        self.set_from(from);
135        self
136    }
137
138    /// Get the kind of transaction.
139    fn kind(&self) -> Option<TxKind>;
140
141    /// Clear the kind of transaction.
142    fn clear_kind(&mut self);
143
144    /// Set the kind of transaction.
145    fn set_kind(&mut self, kind: TxKind);
146
147    /// Builder-pattern method for setting the kind of transaction.
148    fn with_kind(mut self, kind: TxKind) -> Self {
149        self.set_kind(kind);
150        self
151    }
152
153    /// Get the recipient for the transaction.
154    fn to(&self) -> Option<Address> {
155        if let Some(TxKind::Call(addr)) = self.kind() {
156            return Some(addr);
157        }
158        None
159    }
160
161    /// Set the recipient for the transaction.
162    fn set_to(&mut self, to: Address) {
163        self.set_kind(to.into());
164    }
165
166    /// Builder-pattern method for setting the recipient.
167    fn with_to(mut self, to: Address) -> Self {
168        self.set_to(to);
169        self
170    }
171
172    /// Set the `to` field to a create call.
173    fn set_create(&mut self) {
174        self.set_kind(TxKind::Create);
175    }
176
177    /// Set the `to` field to a create call.
178    fn into_create(mut self) -> Self {
179        self.set_create();
180        self
181    }
182
183    /// Deploy the code by making a create call with data. This will set the
184    /// `to` field to [`TxKind::Create`].
185    fn set_deploy_code<T: Into<Bytes>>(&mut self, code: T) {
186        self.set_input(code.into());
187        self.set_create()
188    }
189
190    /// Deploy the code by making a create call with data. This will set the
191    /// `to` field to [`TxKind::Create`].
192    fn with_deploy_code<T: Into<Bytes>>(mut self, code: T) -> Self {
193        self.set_deploy_code(code);
194        self
195    }
196
197    /// Set the data field to a contract call. This will clear the `to` field
198    /// if it is set to [`TxKind::Create`].
199    fn set_call<T: SolCall>(&mut self, t: &T) {
200        self.set_input(t.abi_encode());
201        if matches!(self.kind(), Some(TxKind::Create)) {
202            self.clear_kind();
203        }
204    }
205
206    /// Make a contract call with data.
207    fn with_call<T: SolCall>(mut self, t: &T) -> Self {
208        self.set_call(t);
209        self
210    }
211
212    /// Calculates the address that will be created by the transaction, if any.
213    ///
214    /// Returns `None` if the transaction is not a contract creation (the `to` field is set), or if
215    /// the `from` or `nonce` fields are not set.
216    fn calculate_create_address(&self) -> Option<Address> {
217        if !self.kind().is_some_and(|to| to.is_create()) {
218            return None;
219        }
220        let from = self.from()?;
221        let nonce = self.nonce()?;
222        Some(from.create(nonce))
223    }
224
225    /// Get the value for the transaction.
226    fn value(&self) -> Option<U256>;
227
228    /// Set the value for the transaction.
229    fn set_value(&mut self, value: U256);
230
231    /// Builder-pattern method for setting the value.
232    fn with_value(mut self, value: U256) -> Self {
233        self.set_value(value);
234        self
235    }
236
237    /// Get the legacy gas price for the transaction.
238    fn gas_price(&self) -> Option<u128>;
239
240    /// Set the legacy gas price for the transaction.
241    fn set_gas_price(&mut self, gas_price: u128);
242
243    /// Builder-pattern method for setting the legacy gas price.
244    fn with_gas_price(mut self, gas_price: u128) -> Self {
245        self.set_gas_price(gas_price);
246        self
247    }
248
249    /// Get the max fee per gas for the transaction.
250    fn max_fee_per_gas(&self) -> Option<u128>;
251
252    /// Set the max fee per gas  for the transaction.
253    fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128);
254
255    /// Builder-pattern method for setting max fee per gas .
256    fn with_max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
257        self.set_max_fee_per_gas(max_fee_per_gas);
258        self
259    }
260
261    /// Get the max priority fee per gas for the transaction.
262    fn max_priority_fee_per_gas(&self) -> Option<u128>;
263
264    /// Set the max priority fee per gas for the transaction.
265    fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128);
266
267    /// Builder-pattern method for setting max priority fee per gas.
268    fn with_max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
269        self.set_max_priority_fee_per_gas(max_priority_fee_per_gas);
270        self
271    }
272    /// Get the gas limit for the transaction.
273    fn gas_limit(&self) -> Option<u64>;
274
275    /// Set the gas limit for the transaction.
276    fn set_gas_limit(&mut self, gas_limit: u64);
277
278    /// Builder-pattern method for setting the gas limit.
279    fn with_gas_limit(mut self, gas_limit: u64) -> Self {
280        self.set_gas_limit(gas_limit);
281        self
282    }
283
284    /// Get the EIP-2930 access list for the transaction.
285    fn access_list(&self) -> Option<&AccessList>;
286
287    /// Sets the EIP-2930 access list.
288    fn set_access_list(&mut self, access_list: AccessList);
289
290    /// Builder-pattern method for setting the access list.
291    fn with_access_list(mut self, access_list: AccessList) -> Self {
292        self.set_access_list(access_list);
293        self
294    }
295
296    /// Check if all necessary keys are present to build the specified type,
297    /// returning a list of missing keys.
298    fn complete_type(&self, ty: N::TxType) -> Result<(), Vec<&'static str>>;
299
300    /// Check if all necessary keys are present to build the currently-preferred
301    /// transaction type, returning a list of missing keys.
302    fn complete_preferred(&self) -> Result<(), Vec<&'static str>> {
303        self.complete_type(self.output_tx_type())
304    }
305
306    /// Assert that the builder prefers a certain transaction type. This does
307    /// not indicate that the builder is ready to build. This function uses a
308    /// `dbg_assert_eq!` to check the builder status, and will have no affect
309    /// in release builds.
310    fn assert_preferred(&self, ty: N::TxType) {
311        debug_assert_eq!(self.output_tx_type(), ty);
312    }
313
314    /// Assert that the builder prefers a certain transaction type. This does
315    /// not indicate that the builder is ready to build. This function uses a
316    /// `dbg_assert_eq!` to check the builder status, and will have no affect
317    /// in release builds.
318    fn assert_preferred_chained(self, ty: N::TxType) -> Self {
319        self.assert_preferred(ty);
320        self
321    }
322
323    /// Apply a function to the builder, returning the modified builder.
324    fn apply<F>(self, f: F) -> Self
325    where
326        F: FnOnce(Self) -> Self,
327    {
328        f(self)
329    }
330
331    /// Apply a fallible function to the builder, returning the modified builder or an error.
332    fn try_apply<F, E>(self, f: F) -> Result<Self, E>
333    where
334        F: FnOnce(Self) -> Result<Self, E>,
335    {
336        f(self)
337    }
338
339    /// True if the builder contains all necessary information to be submitted
340    /// to the `eth_sendTransaction` endpoint.
341    fn can_submit(&self) -> bool;
342
343    /// True if the builder contains all necessary information to be built into
344    /// a valid transaction.
345    fn can_build(&self) -> bool;
346
347    /// Returns the transaction type that this builder will attempt to build.
348    /// This does not imply that the builder is ready to build.
349    #[doc(alias = "output_transaction_type")]
350    fn output_tx_type(&self) -> N::TxType;
351
352    /// Returns the transaction type that this builder will build. `None` if
353    /// the builder is not ready to build.
354    #[doc(alias = "output_transaction_type_checked")]
355    fn output_tx_type_checked(&self) -> Option<N::TxType>;
356
357    /// Trim any conflicting keys and populate any computed fields (like blob
358    /// hashes).
359    ///
360    /// This is useful for transaction requests that have multiple conflicting
361    /// fields. While these may be buildable, they may not be submitted to the
362    /// RPC. This method should be called before RPC submission, but is not
363    /// necessary before building.
364    fn prep_for_submission(&mut self);
365
366    /// Build an unsigned, but typed, transaction.
367    fn build_unsigned(self) -> BuildResult<N::UnsignedTx, N>;
368
369    /// Build a signed transaction.
370    fn build<W: NetworkWallet<N>>(
371        self,
372        wallet: &W,
373    ) -> impl_future!(<Output = Result<N::TxEnvelope, TransactionBuilderError<N>>>);
374}