alloy_network/transaction/
builder.rs1use super::signer::NetworkWallet;
2use crate::Network;
3pub use alloy_network_primitives::{
4 TransactionBuilder4844, TransactionBuilder7594, TransactionBuilder7702,
5};
6use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256};
7use alloy_rpc_types_eth::{AccessList, TransactionInputKind};
8use alloy_sol_types::SolCall;
9use futures_utils_wasm::impl_future;
10
11pub type BuildResult<T, N> = Result<T, UnbuiltTransactionError<N>>;
13
14#[derive(Debug, thiserror::Error)]
16#[error("Failed to build transaction: {error}")]
17pub struct UnbuiltTransactionError<N: Network> {
18 pub request: N::TransactionRequest,
20 #[source]
22 pub error: TransactionBuilderError<N>,
23}
24
25#[derive(Debug, thiserror::Error)]
27pub enum TransactionBuilderError<N: Network> {
28 #[error("{0} transaction can't be built due to missing keys: {1:?}")]
30 InvalidTransactionRequest(N::TxType, Vec<&'static str>),
31
32 #[error("Signer cannot produce signature type required for transaction")]
34 UnsupportedSignatureType,
35
36 #[error(transparent)]
38 Signer(#[from] alloy_signer::Error),
39
40 #[error("{0}")]
42 Custom(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
43}
44
45impl<N: Network> TransactionBuilderError<N> {
46 pub fn custom<E>(e: E) -> Self
48 where
49 E: std::error::Error + Send + Sync + 'static,
50 {
51 Self::Custom(Box::new(e))
52 }
53
54 pub const fn into_unbuilt(self, request: N::TransactionRequest) -> UnbuiltTransactionError<N> {
56 UnbuiltTransactionError { request, error: self }
57 }
58}
59
60#[doc(alias = "TxBuilder")]
69pub trait TransactionBuilder<N: Network>: Default + Sized + Send + Sync + 'static {
70 fn chain_id(&self) -> Option<ChainId>;
72
73 fn set_chain_id(&mut self, chain_id: ChainId);
75
76 fn with_chain_id(mut self, chain_id: ChainId) -> Self {
78 self.set_chain_id(chain_id);
79 self
80 }
81
82 fn nonce(&self) -> Option<u64>;
84
85 fn set_nonce(&mut self, nonce: u64);
87
88 fn take_nonce(&mut self) -> Option<u64>;
90
91 fn with_nonce(mut self, nonce: u64) -> Self {
93 self.set_nonce(nonce);
94 self
95 }
96
97 fn without_nonce(mut self) -> Self {
99 self.take_nonce();
100 self
101 }
102
103 fn input(&self) -> Option<&Bytes>;
105
106 fn set_input<T: Into<Bytes>>(&mut self, input: T);
108
109 fn with_input<T: Into<Bytes>>(mut self, input: T) -> Self {
111 self.set_input(input);
112 self
113 }
114
115 fn set_input_kind<T: Into<Bytes>>(&mut self, input: T, _: TransactionInputKind) {
117 self.set_input(input);
119 }
120
121 fn with_input_kind<T: Into<Bytes>>(mut self, input: T, kind: TransactionInputKind) -> Self {
123 self.set_input_kind(input, kind);
124 self
125 }
126
127 fn from(&self) -> Option<Address>;
129
130 fn set_from(&mut self, from: Address);
132
133 fn with_from(mut self, from: Address) -> Self {
135 self.set_from(from);
136 self
137 }
138
139 fn kind(&self) -> Option<TxKind>;
141
142 fn clear_kind(&mut self);
144
145 fn set_kind(&mut self, kind: TxKind);
147
148 fn with_kind(mut self, kind: TxKind) -> Self {
150 self.set_kind(kind);
151 self
152 }
153
154 fn to(&self) -> Option<Address> {
156 if let Some(TxKind::Call(addr)) = self.kind() {
157 return Some(addr);
158 }
159 None
160 }
161
162 fn set_to(&mut self, to: Address) {
164 self.set_kind(to.into());
165 }
166
167 fn with_to(mut self, to: Address) -> Self {
169 self.set_to(to);
170 self
171 }
172
173 fn set_create(&mut self) {
175 self.set_kind(TxKind::Create);
176 }
177
178 fn into_create(mut self) -> Self {
180 self.set_create();
181 self
182 }
183
184 fn set_deploy_code<T: Into<Bytes>>(&mut self, code: T) {
187 self.set_input(code.into());
188 self.set_create()
189 }
190
191 fn with_deploy_code<T: Into<Bytes>>(mut self, code: T) -> Self {
194 self.set_deploy_code(code);
195 self
196 }
197
198 fn set_call<T: SolCall>(&mut self, t: &T) {
201 self.set_input(t.abi_encode());
202 if matches!(self.kind(), Some(TxKind::Create)) {
203 self.clear_kind();
204 }
205 }
206
207 fn with_call<T: SolCall>(mut self, t: &T) -> Self {
209 self.set_call(t);
210 self
211 }
212
213 fn calculate_create_address(&self) -> Option<Address> {
218 if !self.kind().is_some_and(|to| to.is_create()) {
219 return None;
220 }
221 let from = self.from()?;
222 let nonce = self.nonce()?;
223 Some(from.create(nonce))
224 }
225
226 fn value(&self) -> Option<U256>;
228
229 fn set_value(&mut self, value: U256);
231
232 fn with_value(mut self, value: U256) -> Self {
234 self.set_value(value);
235 self
236 }
237
238 fn gas_price(&self) -> Option<u128>;
240
241 fn set_gas_price(&mut self, gas_price: u128);
243
244 fn with_gas_price(mut self, gas_price: u128) -> Self {
246 self.set_gas_price(gas_price);
247 self
248 }
249
250 fn max_fee_per_gas(&self) -> Option<u128>;
252
253 fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128);
255
256 fn with_max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
258 self.set_max_fee_per_gas(max_fee_per_gas);
259 self
260 }
261
262 fn max_priority_fee_per_gas(&self) -> Option<u128>;
264
265 fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128);
267
268 fn with_max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
270 self.set_max_priority_fee_per_gas(max_priority_fee_per_gas);
271 self
272 }
273 fn gas_limit(&self) -> Option<u64>;
275
276 fn set_gas_limit(&mut self, gas_limit: u64);
278
279 fn with_gas_limit(mut self, gas_limit: u64) -> Self {
281 self.set_gas_limit(gas_limit);
282 self
283 }
284
285 fn access_list(&self) -> Option<&AccessList>;
287
288 fn set_access_list(&mut self, access_list: AccessList);
290
291 fn with_access_list(mut self, access_list: AccessList) -> Self {
293 self.set_access_list(access_list);
294 self
295 }
296
297 fn complete_type(&self, ty: N::TxType) -> Result<(), Vec<&'static str>>;
300
301 fn complete_preferred(&self) -> Result<(), Vec<&'static str>> {
304 self.complete_type(self.output_tx_type())
305 }
306
307 fn assert_preferred(&self, ty: N::TxType) {
312 debug_assert_eq!(self.output_tx_type(), ty);
313 }
314
315 fn assert_preferred_chained(self, ty: N::TxType) -> Self {
320 self.assert_preferred(ty);
321 self
322 }
323
324 fn apply<F>(self, f: F) -> Self
326 where
327 F: FnOnce(Self) -> Self,
328 {
329 f(self)
330 }
331
332 fn try_apply<F, E>(self, f: F) -> Result<Self, E>
334 where
335 F: FnOnce(Self) -> Result<Self, E>,
336 {
337 f(self)
338 }
339
340 fn can_submit(&self) -> bool;
343
344 fn can_build(&self) -> bool;
347
348 #[doc(alias = "output_transaction_type")]
351 fn output_tx_type(&self) -> N::TxType;
352
353 #[doc(alias = "output_transaction_type_checked")]
356 fn output_tx_type_checked(&self) -> Option<N::TxType>;
357
358 fn prep_for_submission(&mut self);
366
367 fn build_unsigned(self) -> BuildResult<N::UnsignedTx, N>;
369
370 fn build<W: NetworkWallet<N>>(
372 self,
373 wallet: &W,
374 ) -> impl_future!(<Output = Result<N::TxEnvelope, TransactionBuilderError<N>>>);
375}