alloy_network/transaction/
builder.rs1use super::signer::NetworkWallet;
2use crate::Network;
3use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256};
4use alloy_rpc_types_eth::AccessList;
5use alloy_sol_types::SolCall;
6use futures_utils_wasm::impl_future;
7
8pub use alloy_network_primitives::{TransactionBuilder4844, TransactionBuilder7702};
9
10pub type BuildResult<T, N> = Result<T, UnbuiltTransactionError<N>>;
12
13#[derive(Debug, thiserror::Error)]
15#[error("Failed to build transaction: {error}")]
16pub struct UnbuiltTransactionError<N: Network> {
17    pub request: N::TransactionRequest,
19    #[source]
21    pub error: TransactionBuilderError<N>,
22}
23
24#[derive(Debug, thiserror::Error)]
26pub enum TransactionBuilderError<N: Network> {
27    #[error("{0} transaction can't be built due to missing keys: {1:?}")]
29    InvalidTransactionRequest(N::TxType, Vec<&'static str>),
30
31    #[error("Signer cannot produce signature type required for transaction")]
33    UnsupportedSignatureType,
34
35    #[error(transparent)]
37    Signer(#[from] alloy_signer::Error),
38
39    #[error("{0}")]
41    Custom(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
42}
43
44impl<N: Network> TransactionBuilderError<N> {
45    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    pub const fn into_unbuilt(self, request: N::TransactionRequest) -> UnbuiltTransactionError<N> {
55        UnbuiltTransactionError { request, error: self }
56    }
57}
58
59#[doc(alias = "TxBuilder")]
68pub trait TransactionBuilder<N: Network>: Default + Sized + Send + Sync + 'static {
69    fn chain_id(&self) -> Option<ChainId>;
71
72    fn set_chain_id(&mut self, chain_id: ChainId);
74
75    fn with_chain_id(mut self, chain_id: ChainId) -> Self {
77        self.set_chain_id(chain_id);
78        self
79    }
80
81    fn nonce(&self) -> Option<u64>;
83
84    fn set_nonce(&mut self, nonce: u64);
86
87    fn with_nonce(mut self, nonce: u64) -> Self {
89        self.set_nonce(nonce);
90        self
91    }
92
93    fn input(&self) -> Option<&Bytes>;
95
96    fn set_input<T: Into<Bytes>>(&mut self, input: T);
98
99    fn with_input<T: Into<Bytes>>(mut self, input: T) -> Self {
101        self.set_input(input);
102        self
103    }
104
105    fn from(&self) -> Option<Address>;
107
108    fn set_from(&mut self, from: Address);
110
111    fn with_from(mut self, from: Address) -> Self {
113        self.set_from(from);
114        self
115    }
116
117    fn kind(&self) -> Option<TxKind>;
119
120    fn clear_kind(&mut self);
122
123    fn set_kind(&mut self, kind: TxKind);
125
126    fn with_kind(mut self, kind: TxKind) -> Self {
128        self.set_kind(kind);
129        self
130    }
131
132    fn to(&self) -> Option<Address> {
134        if let Some(TxKind::Call(addr)) = self.kind() {
135            return Some(addr);
136        }
137        None
138    }
139
140    fn set_to(&mut self, to: Address) {
142        self.set_kind(to.into());
143    }
144
145    fn with_to(mut self, to: Address) -> Self {
147        self.set_to(to);
148        self
149    }
150
151    fn set_create(&mut self) {
153        self.set_kind(TxKind::Create);
154    }
155
156    fn into_create(mut self) -> Self {
158        self.set_create();
159        self
160    }
161
162    fn set_deploy_code<T: Into<Bytes>>(&mut self, code: T) {
165        self.set_input(code.into());
166        self.set_create()
167    }
168
169    fn with_deploy_code<T: Into<Bytes>>(mut self, code: T) -> Self {
172        self.set_deploy_code(code);
173        self
174    }
175
176    fn set_call<T: SolCall>(&mut self, t: &T) {
179        self.set_input(t.abi_encode());
180        if matches!(self.kind(), Some(TxKind::Create)) {
181            self.clear_kind();
182        }
183    }
184
185    fn with_call<T: SolCall>(mut self, t: &T) -> Self {
187        self.set_call(t);
188        self
189    }
190
191    fn calculate_create_address(&self) -> Option<Address> {
196        if !self.kind().is_some_and(|to| to.is_create()) {
197            return None;
198        }
199        let from = self.from()?;
200        let nonce = self.nonce()?;
201        Some(from.create(nonce))
202    }
203
204    fn value(&self) -> Option<U256>;
206
207    fn set_value(&mut self, value: U256);
209
210    fn with_value(mut self, value: U256) -> Self {
212        self.set_value(value);
213        self
214    }
215
216    fn gas_price(&self) -> Option<u128>;
218
219    fn set_gas_price(&mut self, gas_price: u128);
221
222    fn with_gas_price(mut self, gas_price: u128) -> Self {
224        self.set_gas_price(gas_price);
225        self
226    }
227
228    fn max_fee_per_gas(&self) -> Option<u128>;
230
231    fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128);
233
234    fn with_max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
236        self.set_max_fee_per_gas(max_fee_per_gas);
237        self
238    }
239
240    fn max_priority_fee_per_gas(&self) -> Option<u128>;
242
243    fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128);
245
246    fn with_max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
248        self.set_max_priority_fee_per_gas(max_priority_fee_per_gas);
249        self
250    }
251    fn gas_limit(&self) -> Option<u64>;
253
254    fn set_gas_limit(&mut self, gas_limit: u64);
256
257    fn with_gas_limit(mut self, gas_limit: u64) -> Self {
259        self.set_gas_limit(gas_limit);
260        self
261    }
262
263    fn access_list(&self) -> Option<&AccessList>;
265
266    fn set_access_list(&mut self, access_list: AccessList);
268
269    fn with_access_list(mut self, access_list: AccessList) -> Self {
271        self.set_access_list(access_list);
272        self
273    }
274
275    fn complete_type(&self, ty: N::TxType) -> Result<(), Vec<&'static str>>;
278
279    fn complete_preferred(&self) -> Result<(), Vec<&'static str>> {
282        self.complete_type(self.output_tx_type())
283    }
284
285    fn assert_preferred(&self, ty: N::TxType) {
290        debug_assert_eq!(self.output_tx_type(), ty);
291    }
292
293    fn assert_preferred_chained(self, ty: N::TxType) -> Self {
298        self.assert_preferred(ty);
299        self
300    }
301
302    fn apply<F>(self, f: F) -> Self
304    where
305        F: FnOnce(Self) -> Self,
306    {
307        f(self)
308    }
309
310    fn try_apply<F, E>(self, f: F) -> Result<Self, E>
312    where
313        F: FnOnce(Self) -> Result<Self, E>,
314    {
315        f(self)
316    }
317
318    fn can_submit(&self) -> bool;
321
322    fn can_build(&self) -> bool;
325
326    #[doc(alias = "output_transaction_type")]
329    fn output_tx_type(&self) -> N::TxType;
330
331    #[doc(alias = "output_transaction_type_checked")]
334    fn output_tx_type_checked(&self) -> Option<N::TxType>;
335
336    fn prep_for_submission(&mut self);
344
345    fn build_unsigned(self) -> BuildResult<N::UnsignedTx, N>;
347
348    fn build<W: NetworkWallet<N>>(
350        self,
351        wallet: &W,
352    ) -> impl_future!(<Output = Result<N::TxEnvelope, TransactionBuilderError<N>>>);
353}