alloy_network/transaction/
builder.rs1use super::signer::NetworkWallet;
2use crate::Network;
3pub use alloy_network_primitives::{TransactionBuilder4844, TransactionBuilder7702};
4use alloy_primitives::{Address, Bytes, ChainId, TxKind, U256};
5use alloy_rpc_types_eth::{AccessList, TransactionInputKind};
6use alloy_sol_types::SolCall;
7use futures_utils_wasm::impl_future;
8
9pub type BuildResult<T, N> = Result<T, UnbuiltTransactionError<N>>;
11
12#[derive(Debug, thiserror::Error)]
14#[error("Failed to build transaction: {error}")]
15pub struct UnbuiltTransactionError<N: Network> {
16 pub request: N::TransactionRequest,
18 #[source]
20 pub error: TransactionBuilderError<N>,
21}
22
23#[derive(Debug, thiserror::Error)]
25pub enum TransactionBuilderError<N: Network> {
26 #[error("{0} transaction can't be built due to missing keys: {1:?}")]
28 InvalidTransactionRequest(N::TxType, Vec<&'static str>),
29
30 #[error("Signer cannot produce signature type required for transaction")]
32 UnsupportedSignatureType,
33
34 #[error(transparent)]
36 Signer(#[from] alloy_signer::Error),
37
38 #[error("{0}")]
40 Custom(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
41}
42
43impl<N: Network> TransactionBuilderError<N> {
44 pub fn custom<E>(e: E) -> Self
46 where
47 E: std::error::Error + Send + Sync + 'static,
48 {
49 Self::Custom(Box::new(e))
50 }
51
52 pub const fn into_unbuilt(self, request: N::TransactionRequest) -> UnbuiltTransactionError<N> {
54 UnbuiltTransactionError { request, error: self }
55 }
56}
57
58#[doc(alias = "TxBuilder")]
60pub trait TransactionBuilder: Default + Sized + Send + Sync + 'static {
61 fn chain_id(&self) -> Option<ChainId>;
63
64 fn set_chain_id(&mut self, chain_id: ChainId);
66
67 fn with_chain_id(mut self, chain_id: ChainId) -> Self {
69 self.set_chain_id(chain_id);
70 self
71 }
72
73 fn nonce(&self) -> Option<u64>;
75
76 fn set_nonce(&mut self, nonce: u64);
78
79 fn take_nonce(&mut self) -> Option<u64>;
81
82 fn with_nonce(mut self, nonce: u64) -> Self {
84 self.set_nonce(nonce);
85 self
86 }
87
88 fn without_nonce(mut self) -> Self {
90 self.take_nonce();
91 self
92 }
93
94 fn input(&self) -> Option<&Bytes>;
96
97 fn set_input<T: Into<Bytes>>(&mut self, input: T);
99
100 fn with_input<T: Into<Bytes>>(mut self, input: T) -> Self {
102 self.set_input(input);
103 self
104 }
105
106 fn set_input_kind<T: Into<Bytes>>(&mut self, input: T, _: TransactionInputKind) {
108 self.set_input(input);
110 }
111
112 fn with_input_kind<T: Into<Bytes>>(mut self, input: T, kind: TransactionInputKind) -> Self {
114 self.set_input_kind(input, kind);
115 self
116 }
117
118 fn from(&self) -> Option<Address>;
120
121 fn set_from(&mut self, from: Address);
123
124 fn with_from(mut self, from: Address) -> Self {
126 self.set_from(from);
127 self
128 }
129
130 fn kind(&self) -> Option<TxKind>;
132
133 fn clear_kind(&mut self);
135
136 fn set_kind(&mut self, kind: TxKind);
138
139 fn with_kind(mut self, kind: TxKind) -> Self {
141 self.set_kind(kind);
142 self
143 }
144
145 fn to(&self) -> Option<Address> {
147 if let Some(TxKind::Call(addr)) = self.kind() {
148 return Some(addr);
149 }
150 None
151 }
152
153 fn set_to(&mut self, to: Address) {
155 self.set_kind(to.into());
156 }
157
158 fn with_to(mut self, to: Address) -> Self {
160 self.set_to(to);
161 self
162 }
163
164 fn set_create(&mut self) {
166 self.set_kind(TxKind::Create);
167 }
168
169 fn into_create(mut self) -> Self {
171 self.set_create();
172 self
173 }
174
175 fn set_deploy_code<T: Into<Bytes>>(&mut self, code: T) {
178 self.set_input(code.into());
179 self.set_create()
180 }
181
182 fn with_deploy_code<T: Into<Bytes>>(mut self, code: T) -> Self {
185 self.set_deploy_code(code);
186 self
187 }
188
189 fn set_call<T: SolCall>(&mut self, t: &T) {
192 self.set_input(t.abi_encode());
193 if matches!(self.kind(), Some(TxKind::Create)) {
194 self.clear_kind();
195 }
196 }
197
198 fn with_call<T: SolCall>(mut self, t: &T) -> Self {
200 self.set_call(t);
201 self
202 }
203
204 fn calculate_create_address(&self) -> Option<Address> {
209 if !self.kind().is_some_and(|to| to.is_create()) {
210 return None;
211 }
212 let from = self.from()?;
213 let nonce = self.nonce()?;
214 Some(from.create(nonce))
215 }
216
217 fn value(&self) -> Option<U256>;
219
220 fn set_value(&mut self, value: U256);
222
223 fn with_value(mut self, value: U256) -> Self {
225 self.set_value(value);
226 self
227 }
228
229 fn gas_price(&self) -> Option<u128>;
231
232 fn set_gas_price(&mut self, gas_price: u128);
234
235 fn with_gas_price(mut self, gas_price: u128) -> Self {
237 self.set_gas_price(gas_price);
238 self
239 }
240
241 fn max_fee_per_gas(&self) -> Option<u128>;
243
244 fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128);
246
247 fn with_max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
249 self.set_max_fee_per_gas(max_fee_per_gas);
250 self
251 }
252
253 fn max_priority_fee_per_gas(&self) -> Option<u128>;
255
256 fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128);
258
259 fn with_max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
261 self.set_max_priority_fee_per_gas(max_priority_fee_per_gas);
262 self
263 }
264 fn gas_limit(&self) -> Option<u64>;
266
267 fn set_gas_limit(&mut self, gas_limit: u64);
269
270 fn with_gas_limit(mut self, gas_limit: u64) -> Self {
272 self.set_gas_limit(gas_limit);
273 self
274 }
275
276 fn access_list(&self) -> Option<&AccessList>;
278
279 fn set_access_list(&mut self, access_list: AccessList);
281
282 fn with_access_list(mut self, access_list: AccessList) -> Self {
284 self.set_access_list(access_list);
285 self
286 }
287
288 fn apply<F>(self, f: F) -> Self
290 where
291 F: FnOnce(Self) -> Self,
292 {
293 f(self)
294 }
295
296 fn try_apply<F, E>(self, f: F) -> Result<Self, E>
298 where
299 F: FnOnce(Self) -> Result<Self, E>,
300 {
301 f(self)
302 }
303}
304
305#[doc(alias = "NetworkTxBuilder")]
310pub trait NetworkTransactionBuilder<N: Network>: TransactionBuilder {
311 fn can_submit(&self) -> bool;
314
315 fn can_build(&self) -> bool;
318
319 fn complete_type(&self, ty: N::TxType) -> Result<(), Vec<&'static str>>;
322
323 fn complete_preferred(&self) -> Result<(), Vec<&'static str>> {
326 self.complete_type(self.output_tx_type())
327 }
328
329 fn assert_preferred(&self, ty: N::TxType) {
334 debug_assert_eq!(self.output_tx_type(), ty);
335 }
336
337 fn assert_preferred_chained(self, ty: N::TxType) -> Self {
342 self.assert_preferred(ty);
343 self
344 }
345
346 #[doc(alias = "output_transaction_type")]
349 fn output_tx_type(&self) -> N::TxType;
350
351 #[doc(alias = "output_transaction_type_checked")]
354 fn output_tx_type_checked(&self) -> Option<N::TxType>;
355
356 fn prep_for_submission(&mut self);
364
365 fn build_unsigned(self) -> BuildResult<N::UnsignedTx, N>;
367
368 fn build<W: NetworkWallet<N>>(
370 self,
371 wallet: &W,
372 ) -> impl_future!(<Output = Result<N::TxEnvelope, TransactionBuilderError<N>>>);
373}