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, TransactionInputKind};
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 take_nonce(&mut self) -> Option<u64>;
89
90 fn with_nonce(mut self, nonce: u64) -> Self {
92 self.set_nonce(nonce);
93 self
94 }
95
96 fn without_nonce(mut self) -> Self {
98 self.take_nonce();
99 self
100 }
101
102 fn input(&self) -> Option<&Bytes>;
104
105 fn set_input<T: Into<Bytes>>(&mut self, input: T);
107
108 fn with_input<T: Into<Bytes>>(mut self, input: T) -> Self {
110 self.set_input(input);
111 self
112 }
113
114 fn set_input_kind<T: Into<Bytes>>(&mut self, input: T, _: TransactionInputKind) {
116 self.set_input(input);
118 }
119
120 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 fn from(&self) -> Option<Address>;
128
129 fn set_from(&mut self, from: Address);
131
132 fn with_from(mut self, from: Address) -> Self {
134 self.set_from(from);
135 self
136 }
137
138 fn kind(&self) -> Option<TxKind>;
140
141 fn clear_kind(&mut self);
143
144 fn set_kind(&mut self, kind: TxKind);
146
147 fn with_kind(mut self, kind: TxKind) -> Self {
149 self.set_kind(kind);
150 self
151 }
152
153 fn to(&self) -> Option<Address> {
155 if let Some(TxKind::Call(addr)) = self.kind() {
156 return Some(addr);
157 }
158 None
159 }
160
161 fn set_to(&mut self, to: Address) {
163 self.set_kind(to.into());
164 }
165
166 fn with_to(mut self, to: Address) -> Self {
168 self.set_to(to);
169 self
170 }
171
172 fn set_create(&mut self) {
174 self.set_kind(TxKind::Create);
175 }
176
177 fn into_create(mut self) -> Self {
179 self.set_create();
180 self
181 }
182
183 fn set_deploy_code<T: Into<Bytes>>(&mut self, code: T) {
186 self.set_input(code.into());
187 self.set_create()
188 }
189
190 fn with_deploy_code<T: Into<Bytes>>(mut self, code: T) -> Self {
193 self.set_deploy_code(code);
194 self
195 }
196
197 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 fn with_call<T: SolCall>(mut self, t: &T) -> Self {
208 self.set_call(t);
209 self
210 }
211
212 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 fn value(&self) -> Option<U256>;
227
228 fn set_value(&mut self, value: U256);
230
231 fn with_value(mut self, value: U256) -> Self {
233 self.set_value(value);
234 self
235 }
236
237 fn gas_price(&self) -> Option<u128>;
239
240 fn set_gas_price(&mut self, gas_price: u128);
242
243 fn with_gas_price(mut self, gas_price: u128) -> Self {
245 self.set_gas_price(gas_price);
246 self
247 }
248
249 fn max_fee_per_gas(&self) -> Option<u128>;
251
252 fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128);
254
255 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 fn max_priority_fee_per_gas(&self) -> Option<u128>;
263
264 fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128);
266
267 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 fn gas_limit(&self) -> Option<u64>;
274
275 fn set_gas_limit(&mut self, gas_limit: u64);
277
278 fn with_gas_limit(mut self, gas_limit: u64) -> Self {
280 self.set_gas_limit(gas_limit);
281 self
282 }
283
284 fn access_list(&self) -> Option<&AccessList>;
286
287 fn set_access_list(&mut self, access_list: AccessList);
289
290 fn with_access_list(mut self, access_list: AccessList) -> Self {
292 self.set_access_list(access_list);
293 self
294 }
295
296 fn complete_type(&self, ty: N::TxType) -> Result<(), Vec<&'static str>>;
299
300 fn complete_preferred(&self) -> Result<(), Vec<&'static str>> {
303 self.complete_type(self.output_tx_type())
304 }
305
306 fn assert_preferred(&self, ty: N::TxType) {
311 debug_assert_eq!(self.output_tx_type(), ty);
312 }
313
314 fn assert_preferred_chained(self, ty: N::TxType) -> Self {
319 self.assert_preferred(ty);
320 self
321 }
322
323 fn apply<F>(self, f: F) -> Self
325 where
326 F: FnOnce(Self) -> Self,
327 {
328 f(self)
329 }
330
331 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 fn can_submit(&self) -> bool;
342
343 fn can_build(&self) -> bool;
346
347 #[doc(alias = "output_transaction_type")]
350 fn output_tx_type(&self) -> N::TxType;
351
352 #[doc(alias = "output_transaction_type_checked")]
355 fn output_tx_type_checked(&self) -> Option<N::TxType>;
356
357 fn prep_for_submission(&mut self);
365
366 fn build_unsigned(self) -> BuildResult<N::UnsignedTx, N>;
368
369 fn build<W: NetworkWallet<N>>(
371 self,
372 wallet: &W,
373 ) -> impl_future!(<Output = Result<N::TxEnvelope, TransactionBuilderError<N>>>);
374}