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 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 set_input_kind<T: Into<Bytes>>(&mut self, input: T, _: TransactionInputKind) {
107 self.set_input(input);
109 }
110
111 fn with_input_kind<T: Into<Bytes>>(mut self, input: T, kind: TransactionInputKind) -> Self {
113 self.set_input_kind(input, kind);
114 self
115 }
116
117 fn from(&self) -> Option<Address>;
119
120 fn set_from(&mut self, from: Address);
122
123 fn with_from(mut self, from: Address) -> Self {
125 self.set_from(from);
126 self
127 }
128
129 fn kind(&self) -> Option<TxKind>;
131
132 fn clear_kind(&mut self);
134
135 fn set_kind(&mut self, kind: TxKind);
137
138 fn with_kind(mut self, kind: TxKind) -> Self {
140 self.set_kind(kind);
141 self
142 }
143
144 fn to(&self) -> Option<Address> {
146 if let Some(TxKind::Call(addr)) = self.kind() {
147 return Some(addr);
148 }
149 None
150 }
151
152 fn set_to(&mut self, to: Address) {
154 self.set_kind(to.into());
155 }
156
157 fn with_to(mut self, to: Address) -> Self {
159 self.set_to(to);
160 self
161 }
162
163 fn set_create(&mut self) {
165 self.set_kind(TxKind::Create);
166 }
167
168 fn into_create(mut self) -> Self {
170 self.set_create();
171 self
172 }
173
174 fn set_deploy_code<T: Into<Bytes>>(&mut self, code: T) {
177 self.set_input(code.into());
178 self.set_create()
179 }
180
181 fn with_deploy_code<T: Into<Bytes>>(mut self, code: T) -> Self {
184 self.set_deploy_code(code);
185 self
186 }
187
188 fn set_call<T: SolCall>(&mut self, t: &T) {
191 self.set_input(t.abi_encode());
192 if matches!(self.kind(), Some(TxKind::Create)) {
193 self.clear_kind();
194 }
195 }
196
197 fn with_call<T: SolCall>(mut self, t: &T) -> Self {
199 self.set_call(t);
200 self
201 }
202
203 fn calculate_create_address(&self) -> Option<Address> {
208 if !self.kind().is_some_and(|to| to.is_create()) {
209 return None;
210 }
211 let from = self.from()?;
212 let nonce = self.nonce()?;
213 Some(from.create(nonce))
214 }
215
216 fn value(&self) -> Option<U256>;
218
219 fn set_value(&mut self, value: U256);
221
222 fn with_value(mut self, value: U256) -> Self {
224 self.set_value(value);
225 self
226 }
227
228 fn gas_price(&self) -> Option<u128>;
230
231 fn set_gas_price(&mut self, gas_price: u128);
233
234 fn with_gas_price(mut self, gas_price: u128) -> Self {
236 self.set_gas_price(gas_price);
237 self
238 }
239
240 fn max_fee_per_gas(&self) -> Option<u128>;
242
243 fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128);
245
246 fn with_max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
248 self.set_max_fee_per_gas(max_fee_per_gas);
249 self
250 }
251
252 fn max_priority_fee_per_gas(&self) -> Option<u128>;
254
255 fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128);
257
258 fn with_max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
260 self.set_max_priority_fee_per_gas(max_priority_fee_per_gas);
261 self
262 }
263 fn gas_limit(&self) -> Option<u64>;
265
266 fn set_gas_limit(&mut self, gas_limit: u64);
268
269 fn with_gas_limit(mut self, gas_limit: u64) -> Self {
271 self.set_gas_limit(gas_limit);
272 self
273 }
274
275 fn access_list(&self) -> Option<&AccessList>;
277
278 fn set_access_list(&mut self, access_list: AccessList);
280
281 fn with_access_list(mut self, access_list: AccessList) -> Self {
283 self.set_access_list(access_list);
284 self
285 }
286
287 fn complete_type(&self, ty: N::TxType) -> Result<(), Vec<&'static str>>;
290
291 fn complete_preferred(&self) -> Result<(), Vec<&'static str>> {
294 self.complete_type(self.output_tx_type())
295 }
296
297 fn assert_preferred(&self, ty: N::TxType) {
302 debug_assert_eq!(self.output_tx_type(), ty);
303 }
304
305 fn assert_preferred_chained(self, ty: N::TxType) -> Self {
310 self.assert_preferred(ty);
311 self
312 }
313
314 fn apply<F>(self, f: F) -> Self
316 where
317 F: FnOnce(Self) -> Self,
318 {
319 f(self)
320 }
321
322 fn try_apply<F, E>(self, f: F) -> Result<Self, E>
324 where
325 F: FnOnce(Self) -> Result<Self, E>,
326 {
327 f(self)
328 }
329
330 fn can_submit(&self) -> bool;
333
334 fn can_build(&self) -> bool;
337
338 #[doc(alias = "output_transaction_type")]
341 fn output_tx_type(&self) -> N::TxType;
342
343 #[doc(alias = "output_transaction_type_checked")]
346 fn output_tx_type_checked(&self) -> Option<N::TxType>;
347
348 fn prep_for_submission(&mut self);
356
357 fn build_unsigned(self) -> BuildResult<N::UnsignedTx, N>;
359
360 fn build<W: NetworkWallet<N>>(
362 self,
363 wallet: &W,
364 ) -> impl_future!(<Output = Result<N::TxEnvelope, TransactionBuilderError<N>>>);
365}