foundry_common/transactions/
builder.rs1use std::num::NonZeroU64;
2
3use alloy_consensus::{
4 BlobTransactionSidecar, BlobTransactionSidecarEip7594, BlobTransactionSidecarVariant,
5};
6use alloy_eips::{Encodable2718, eip7702::SignedAuthorization};
7use alloy_network::{AnyNetwork, Ethereum, Network, NetworkTransactionBuilder};
8use alloy_primitives::{Address, B256, Signature, TxKind, U256};
9use alloy_provider::Provider;
10use alloy_signer::Signer;
11use eyre::Result;
12#[cfg(feature = "optimism")]
13use op_alloy_network::Optimism;
14#[cfg(feature = "optimism")]
15use op_alloy_rpc_types::OpTransactionRequest;
16use tempo_alloy::{TempoNetwork, provider::TempoProviderExt};
17use tempo_primitives::{
18 TempoSignature,
19 transaction::{Call, KeychainSignature, PrimitiveSignature, SignedKeyAuthorization},
20};
21
22pub trait FoundryTransactionBuilder<N: Network>: NetworkTransactionBuilder<N> {
50 fn reset_gas_limit(&mut self);
52
53 fn max_fee_per_blob_gas(&self) -> Option<u128> {
55 None
56 }
57
58 fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
60
61 fn with_max_fee_per_blob_gas(mut self, max_fee_per_blob_gas: u128) -> Self {
63 self.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
64 self
65 }
66
67 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
72 None
73 }
74
75 fn set_blob_versioned_hashes(&mut self, _hashes: Vec<B256>) {}
77
78 fn with_blob_versioned_hashes(mut self, hashes: Vec<B256>) -> Self {
80 self.set_blob_versioned_hashes(hashes);
81 self
82 }
83
84 fn blob_sidecar(&self) -> Option<&BlobTransactionSidecarVariant> {
86 None
87 }
88
89 fn set_blob_sidecar(&mut self, _sidecar: BlobTransactionSidecarVariant) {}
94
95 fn with_blob_sidecar(mut self, sidecar: BlobTransactionSidecarVariant) -> Self {
97 self.set_blob_sidecar(sidecar);
98 self
99 }
100
101 fn blob_sidecar_4844(&self) -> Option<&BlobTransactionSidecar> {
103 self.blob_sidecar().and_then(|s| s.as_eip4844())
104 }
105
106 fn set_blob_sidecar_4844(&mut self, sidecar: BlobTransactionSidecar) {
108 self.set_blob_sidecar(BlobTransactionSidecarVariant::Eip4844(sidecar));
109 }
110
111 fn with_blob_sidecar_4844(mut self, sidecar: BlobTransactionSidecar) -> Self {
113 self.set_blob_sidecar_4844(sidecar);
114 self
115 }
116
117 fn blob_sidecar_7594(&self) -> Option<&BlobTransactionSidecarEip7594> {
119 self.blob_sidecar().and_then(|s| s.as_eip7594())
120 }
121
122 fn set_blob_sidecar_7594(&mut self, sidecar: BlobTransactionSidecarEip7594) {
124 self.set_blob_sidecar(BlobTransactionSidecarVariant::Eip7594(sidecar));
125 }
126
127 fn with_blob_sidecar_7594(mut self, sidecar: BlobTransactionSidecarEip7594) -> Self {
129 self.set_blob_sidecar_7594(sidecar);
130 self
131 }
132
133 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
135 None
136 }
137
138 fn set_authorization_list(&mut self, _authorization_list: Vec<SignedAuthorization>) {}
140
141 fn with_authorization_list(mut self, authorization_list: Vec<SignedAuthorization>) -> Self {
143 self.set_authorization_list(authorization_list);
144 self
145 }
146
147 fn fee_token(&self) -> Option<Address> {
149 None
150 }
151
152 fn set_fee_token(&mut self, _fee_token: Address) {}
154
155 fn with_fee_token(mut self, fee_token: Address) -> Self {
157 self.set_fee_token(fee_token);
158 self
159 }
160
161 fn nonce_key(&self) -> Option<U256> {
163 None
164 }
165
166 fn set_nonce_key(&mut self, _nonce_key: U256) {}
168
169 fn with_nonce_key(mut self, nonce_key: U256) -> Self {
171 self.set_nonce_key(nonce_key);
172 self
173 }
174
175 fn key_id(&self) -> Option<Address> {
177 None
178 }
179
180 fn set_key_id(&mut self, _key_id: Address) {}
185
186 fn with_key_id(mut self, key_id: Address) -> Self {
188 self.set_key_id(key_id);
189 self
190 }
191
192 fn valid_before(&self) -> Option<NonZeroU64> {
194 None
195 }
196
197 fn set_valid_before(&mut self, _valid_before: NonZeroU64) {}
199
200 fn with_valid_before(mut self, valid_before: NonZeroU64) -> Self {
202 self.set_valid_before(valid_before);
203 self
204 }
205
206 fn valid_after(&self) -> Option<NonZeroU64> {
208 None
209 }
210
211 fn set_valid_after(&mut self, _valid_after: NonZeroU64) {}
213
214 fn with_valid_after(mut self, valid_after: NonZeroU64) -> Self {
216 self.set_valid_after(valid_after);
217 self
218 }
219
220 fn fee_payer_signature(&self) -> Option<Signature> {
222 None
223 }
224
225 fn set_fee_payer_signature(&mut self, _signature: Signature) {}
227
228 fn with_fee_payer_signature(mut self, signature: Signature) -> Self {
230 self.set_fee_payer_signature(signature);
231 self
232 }
233
234 fn compute_sponsor_hash(&self, _from: Address) -> Option<B256> {
240 None
241 }
242
243 fn set_key_authorization(&mut self, _key_authorization: SignedKeyAuthorization) {}
248
249 fn prepare_access_key_authorization<'a>(
255 &'a mut self,
256 _provider: &'a impl Provider<N>,
257 _wallet_address: Address,
258 _key_address: Address,
259 _key_authorization: Option<&'a SignedKeyAuthorization>,
260 ) -> impl Future<Output = Result<()>> + Send + 'a
261 where
262 Self: Send,
263 {
264 async { Ok(()) }
265 }
266
267 fn convert_create_to_call(&mut self) {}
273
274 fn clear_batch_to(&mut self) {}
281
282 fn sign_with_access_key(
289 self,
290 _provider: &impl Provider<N>,
291 _signer: &(impl Signer + Sync),
292 _wallet_address: Address,
293 _key_address: Address,
294 _key_authorization: Option<&SignedKeyAuthorization>,
295 ) -> impl Future<Output = Result<Vec<u8>>> + Send {
296 async { eyre::bail!("access key signing is not supported for this network") }
297 }
298}
299
300impl FoundryTransactionBuilder<Ethereum> for <Ethereum as Network>::TransactionRequest {
301 fn reset_gas_limit(&mut self) {
302 self.gas = None;
303 }
304
305 fn max_fee_per_blob_gas(&self) -> Option<u128> {
306 self.max_fee_per_blob_gas
307 }
308
309 fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
310 self.max_fee_per_blob_gas = Some(max_fee_per_blob_gas);
311 }
312
313 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
314 self.blob_versioned_hashes.as_deref()
315 }
316
317 fn set_blob_versioned_hashes(&mut self, hashes: Vec<B256>) {
318 self.blob_versioned_hashes = Some(hashes);
319 }
320
321 fn blob_sidecar(&self) -> Option<&BlobTransactionSidecarVariant> {
322 self.sidecar.as_ref()
323 }
324
325 fn set_blob_sidecar(&mut self, sidecar: BlobTransactionSidecarVariant) {
326 self.sidecar = Some(sidecar);
327 self.populate_blob_hashes();
328 }
329
330 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
331 self.authorization_list.as_ref()
332 }
333
334 fn set_authorization_list(&mut self, authorization_list: Vec<SignedAuthorization>) {
335 self.authorization_list = Some(authorization_list);
336 }
337}
338
339impl FoundryTransactionBuilder<AnyNetwork> for <AnyNetwork as Network>::TransactionRequest {
340 fn reset_gas_limit(&mut self) {
341 self.gas = None;
342 }
343
344 fn max_fee_per_blob_gas(&self) -> Option<u128> {
345 self.max_fee_per_blob_gas
346 }
347
348 fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
349 self.max_fee_per_blob_gas = Some(max_fee_per_blob_gas);
350 }
351
352 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
353 self.blob_versioned_hashes.as_deref()
354 }
355
356 fn set_blob_versioned_hashes(&mut self, hashes: Vec<B256>) {
357 self.blob_versioned_hashes = Some(hashes);
358 }
359
360 fn blob_sidecar(&self) -> Option<&BlobTransactionSidecarVariant> {
361 self.sidecar.as_ref()
362 }
363
364 fn set_blob_sidecar(&mut self, sidecar: BlobTransactionSidecarVariant) {
365 self.sidecar = Some(sidecar);
366 self.populate_blob_hashes();
367 }
368
369 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
370 self.authorization_list.as_ref()
371 }
372
373 fn set_authorization_list(&mut self, authorization_list: Vec<SignedAuthorization>) {
374 self.authorization_list = Some(authorization_list);
375 }
376}
377
378#[cfg(feature = "optimism")]
379impl FoundryTransactionBuilder<Optimism> for OpTransactionRequest {
380 fn reset_gas_limit(&mut self) {
381 self.as_mut().gas = None;
382 }
383
384 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
385 self.as_ref().authorization_list.as_ref()
386 }
387
388 fn set_authorization_list(&mut self, authorization_list: Vec<SignedAuthorization>) {
389 self.as_mut().authorization_list = Some(authorization_list);
390 }
391}
392
393impl FoundryTransactionBuilder<TempoNetwork> for <TempoNetwork as Network>::TransactionRequest {
394 fn reset_gas_limit(&mut self) {
395 self.gas = None;
396 }
397
398 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
399 self.authorization_list.as_ref()
400 }
401
402 fn set_authorization_list(&mut self, authorization_list: Vec<SignedAuthorization>) {
403 self.authorization_list = Some(authorization_list);
404 }
405
406 fn fee_token(&self) -> Option<Address> {
407 self.fee_token
408 }
409
410 fn set_fee_token(&mut self, fee_token: Address) {
411 self.fee_token = Some(fee_token);
412 }
413
414 fn nonce_key(&self) -> Option<U256> {
415 self.nonce_key
416 }
417
418 fn set_nonce_key(&mut self, nonce_key: U256) {
419 self.nonce_key = Some(nonce_key);
420 }
421
422 fn key_id(&self) -> Option<Address> {
423 self.key_id
424 }
425
426 fn set_key_id(&mut self, key_id: Address) {
427 self.key_id = Some(key_id);
428 }
429
430 fn valid_before(&self) -> Option<NonZeroU64> {
431 self.valid_before
432 }
433
434 fn set_valid_before(&mut self, valid_before: NonZeroU64) {
435 self.valid_before = Some(valid_before);
436 }
437
438 fn valid_after(&self) -> Option<NonZeroU64> {
439 self.valid_after
440 }
441
442 fn set_valid_after(&mut self, valid_after: NonZeroU64) {
443 self.valid_after = Some(valid_after);
444 }
445
446 fn fee_payer_signature(&self) -> Option<Signature> {
447 self.fee_payer_signature
448 }
449
450 fn set_fee_payer_signature(&mut self, signature: Signature) {
451 self.fee_payer_signature = Some(signature);
452 }
453
454 fn compute_sponsor_hash(&self, from: Address) -> Option<B256> {
455 let tx = self.clone().build_aa().ok()?;
456 Some(tx.fee_payer_signature_hash(from))
457 }
458
459 fn set_key_authorization(&mut self, key_authorization: SignedKeyAuthorization) {
460 self.key_authorization = Some(key_authorization);
461 }
462
463 fn prepare_access_key_authorization<'a>(
464 &'a mut self,
465 provider: &'a impl Provider<TempoNetwork>,
466 wallet_address: Address,
467 key_address: Address,
468 key_authorization: Option<&'a SignedKeyAuthorization>,
469 ) -> impl Future<Output = Result<()>> + Send + 'a
470 where
471 Self: Send,
472 {
473 let auth = key_authorization.cloned();
474
475 async move {
476 if let Some(auth) = auth {
477 let is_provisioned = provider
478 .get_keychain_key(wallet_address, key_address)
479 .await
480 .map(|info| info.keyId != Address::ZERO)
481 .unwrap_or(false);
482
483 if !is_provisioned {
484 self.set_key_authorization(auth);
485 }
486 }
487
488 Ok(())
489 }
490 }
491
492 fn convert_create_to_call(&mut self) {
493 if self.calls.is_empty() && self.inner.to.is_some_and(|to| to.is_create()) {
494 let input = self.inner.input.input().cloned().unwrap_or_default();
495 let value = self.inner.value.unwrap_or(U256::ZERO);
496 self.calls.push(Call { to: TxKind::Create, value, input });
497 self.inner.input = Default::default();
498 self.inner.value = None;
499 self.inner.to = None;
500 }
501 }
502
503 fn clear_batch_to(&mut self) {
504 if !self.calls.is_empty() {
505 self.inner.to = None;
506 self.inner.value = None;
507 }
508 }
509
510 fn sign_with_access_key(
511 mut self,
512 provider: &impl Provider<TempoNetwork>,
513 signer: &(impl Signer + Sync),
514 wallet_address: Address,
515 key_address: Address,
516 key_authorization: Option<&SignedKeyAuthorization>,
517 ) -> impl Future<Output = Result<Vec<u8>>> + Send {
518 let auth = key_authorization.cloned();
519 let provisioning_fut = provider.get_keychain_key(wallet_address, key_address);
520
521 async move {
522 if let Some(auth) = auth {
523 let is_provisioned =
524 provisioning_fut.await.map(|info| info.keyId != Address::ZERO).unwrap_or(false);
525
526 if !is_provisioned && self.key_authorization.is_none() {
527 if self.fee_payer_signature.is_some() {
528 eyre::bail!(
529 "cannot add Tempo key authorization after fee payer signature was attached"
530 );
531 }
532 self.set_key_authorization(auth);
533 }
534 }
535
536 let tempo_tx = self
537 .build_aa()
538 .map_err(|e| eyre::eyre!("failed to build Tempo AA transaction: {e}"))?;
539
540 let sig_hash = tempo_tx.signature_hash();
541 let signing_hash = KeychainSignature::signing_hash(sig_hash, wallet_address);
542 let raw_sig = signer.sign_hash(&signing_hash).await?;
543
544 let keychain_sig =
545 KeychainSignature::new(wallet_address, PrimitiveSignature::Secp256k1(raw_sig));
546 let aa_signed = tempo_tx.into_signed(TempoSignature::Keychain(keychain_sig));
547
548 let mut buf = Vec::new();
549 aa_signed.encode_2718(&mut buf);
550 Ok(buf)
551 }
552 }
553}