foundry_common/transactions/
builder.rs1use alloy_consensus::{
2 BlobTransactionSidecar, BlobTransactionSidecarEip7594, BlobTransactionSidecarVariant,
3};
4use alloy_eips::{Encodable2718, eip7702::SignedAuthorization};
5use alloy_network::{AnyNetwork, Ethereum, Network, TransactionBuilder};
6use alloy_primitives::{Address, B256, Signature, TxKind, U256};
7use alloy_provider::Provider;
8use alloy_signer::Signer;
9use eyre::Result;
10use op_alloy_network::Optimism;
11use op_alloy_rpc_types::OpTransactionRequest;
12use tempo_alloy::{TempoNetwork, provider::TempoProviderExt};
13use tempo_primitives::{
14 TempoSignature,
15 transaction::{Call, KeychainSignature, PrimitiveSignature, SignedKeyAuthorization},
16};
17
18pub trait FoundryTransactionBuilder<N: Network>: TransactionBuilder<N> {
46 fn reset_gas_limit(&mut self);
48
49 fn max_fee_per_blob_gas(&self) -> Option<u128> {
51 None
52 }
53
54 fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
56
57 fn with_max_fee_per_blob_gas(mut self, max_fee_per_blob_gas: u128) -> Self {
59 self.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
60 self
61 }
62
63 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
68 None
69 }
70
71 fn set_blob_versioned_hashes(&mut self, _hashes: Vec<B256>) {}
73
74 fn with_blob_versioned_hashes(mut self, hashes: Vec<B256>) -> Self {
76 self.set_blob_versioned_hashes(hashes);
77 self
78 }
79
80 fn blob_sidecar(&self) -> Option<&BlobTransactionSidecarVariant> {
82 None
83 }
84
85 fn set_blob_sidecar(&mut self, _sidecar: BlobTransactionSidecarVariant) {}
90
91 fn with_blob_sidecar(mut self, sidecar: BlobTransactionSidecarVariant) -> Self {
93 self.set_blob_sidecar(sidecar);
94 self
95 }
96
97 fn blob_sidecar_4844(&self) -> Option<&BlobTransactionSidecar> {
99 self.blob_sidecar().and_then(|s| s.as_eip4844())
100 }
101
102 fn set_blob_sidecar_4844(&mut self, sidecar: BlobTransactionSidecar) {
104 self.set_blob_sidecar(BlobTransactionSidecarVariant::Eip4844(sidecar));
105 }
106
107 fn with_blob_sidecar_4844(mut self, sidecar: BlobTransactionSidecar) -> Self {
109 self.set_blob_sidecar_4844(sidecar);
110 self
111 }
112
113 fn blob_sidecar_7594(&self) -> Option<&BlobTransactionSidecarEip7594> {
115 self.blob_sidecar().and_then(|s| s.as_eip7594())
116 }
117
118 fn set_blob_sidecar_7594(&mut self, sidecar: BlobTransactionSidecarEip7594) {
120 self.set_blob_sidecar(BlobTransactionSidecarVariant::Eip7594(sidecar));
121 }
122
123 fn with_blob_sidecar_7594(mut self, sidecar: BlobTransactionSidecarEip7594) -> Self {
125 self.set_blob_sidecar_7594(sidecar);
126 self
127 }
128
129 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
131 None
132 }
133
134 fn set_authorization_list(&mut self, _authorization_list: Vec<SignedAuthorization>) {}
136
137 fn with_authorization_list(mut self, authorization_list: Vec<SignedAuthorization>) -> Self {
139 self.set_authorization_list(authorization_list);
140 self
141 }
142
143 fn fee_token(&self) -> Option<Address> {
145 None
146 }
147
148 fn set_fee_token(&mut self, _fee_token: Address) {}
150
151 fn with_fee_token(mut self, fee_token: Address) -> Self {
153 self.set_fee_token(fee_token);
154 self
155 }
156
157 fn nonce_key(&self) -> Option<U256> {
159 None
160 }
161
162 fn set_nonce_key(&mut self, _nonce_key: U256) {}
164
165 fn with_nonce_key(mut self, nonce_key: U256) -> Self {
167 self.set_nonce_key(nonce_key);
168 self
169 }
170
171 fn key_id(&self) -> Option<Address> {
173 None
174 }
175
176 fn set_key_id(&mut self, _key_id: Address) {}
181
182 fn with_key_id(mut self, key_id: Address) -> Self {
184 self.set_key_id(key_id);
185 self
186 }
187
188 fn valid_before(&self) -> Option<u64> {
190 None
191 }
192
193 fn set_valid_before(&mut self, _valid_before: u64) {}
195
196 fn with_valid_before(mut self, valid_before: u64) -> Self {
198 self.set_valid_before(valid_before);
199 self
200 }
201
202 fn valid_after(&self) -> Option<u64> {
204 None
205 }
206
207 fn set_valid_after(&mut self, _valid_after: u64) {}
209
210 fn with_valid_after(mut self, valid_after: u64) -> Self {
212 self.set_valid_after(valid_after);
213 self
214 }
215
216 fn fee_payer_signature(&self) -> Option<Signature> {
218 None
219 }
220
221 fn set_fee_payer_signature(&mut self, _signature: Signature) {}
223
224 fn with_fee_payer_signature(mut self, signature: Signature) -> Self {
226 self.set_fee_payer_signature(signature);
227 self
228 }
229
230 fn compute_sponsor_hash(&self, _from: Address) -> Option<B256> {
236 None
237 }
238
239 fn set_key_authorization(&mut self, _key_authorization: SignedKeyAuthorization) {}
244
245 fn convert_create_to_call(&mut self) {}
251
252 fn clear_batch_to(&mut self) {}
259
260 fn sign_with_access_key(
267 self,
268 _provider: &impl Provider<N>,
269 _signer: &(impl Signer + Sync),
270 _wallet_address: Address,
271 _key_address: Address,
272 _key_authorization: Option<&SignedKeyAuthorization>,
273 ) -> impl Future<Output = Result<Vec<u8>>> + Send {
274 async { eyre::bail!("access key signing is not supported for this network") }
275 }
276}
277
278impl FoundryTransactionBuilder<Ethereum> for <Ethereum as Network>::TransactionRequest {
279 fn reset_gas_limit(&mut self) {
280 self.gas = None;
281 }
282
283 fn max_fee_per_blob_gas(&self) -> Option<u128> {
284 self.max_fee_per_blob_gas
285 }
286
287 fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
288 self.max_fee_per_blob_gas = Some(max_fee_per_blob_gas);
289 }
290
291 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
292 self.blob_versioned_hashes.as_deref()
293 }
294
295 fn set_blob_versioned_hashes(&mut self, hashes: Vec<B256>) {
296 self.blob_versioned_hashes = Some(hashes);
297 }
298
299 fn blob_sidecar(&self) -> Option<&BlobTransactionSidecarVariant> {
300 self.sidecar.as_ref()
301 }
302
303 fn set_blob_sidecar(&mut self, sidecar: BlobTransactionSidecarVariant) {
304 self.sidecar = Some(sidecar);
305 self.populate_blob_hashes();
306 }
307
308 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
309 self.authorization_list.as_ref()
310 }
311
312 fn set_authorization_list(&mut self, authorization_list: Vec<SignedAuthorization>) {
313 self.authorization_list = Some(authorization_list);
314 }
315}
316
317impl FoundryTransactionBuilder<AnyNetwork> for <AnyNetwork as Network>::TransactionRequest {
318 fn reset_gas_limit(&mut self) {
319 self.gas = None;
320 }
321
322 fn max_fee_per_blob_gas(&self) -> Option<u128> {
323 self.max_fee_per_blob_gas
324 }
325
326 fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
327 self.max_fee_per_blob_gas = Some(max_fee_per_blob_gas);
328 }
329
330 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
331 self.blob_versioned_hashes.as_deref()
332 }
333
334 fn set_blob_versioned_hashes(&mut self, hashes: Vec<B256>) {
335 self.blob_versioned_hashes = Some(hashes);
336 }
337
338 fn blob_sidecar(&self) -> Option<&BlobTransactionSidecarVariant> {
339 self.sidecar.as_ref()
340 }
341
342 fn set_blob_sidecar(&mut self, sidecar: BlobTransactionSidecarVariant) {
343 self.sidecar = Some(sidecar);
344 self.populate_blob_hashes();
345 }
346
347 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
348 self.authorization_list.as_ref()
349 }
350
351 fn set_authorization_list(&mut self, authorization_list: Vec<SignedAuthorization>) {
352 self.authorization_list = Some(authorization_list);
353 }
354}
355
356impl FoundryTransactionBuilder<Optimism> for OpTransactionRequest {
357 fn reset_gas_limit(&mut self) {
358 self.as_mut().gas = None;
359 }
360
361 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
362 self.as_ref().authorization_list.as_ref()
363 }
364
365 fn set_authorization_list(&mut self, authorization_list: Vec<SignedAuthorization>) {
366 self.as_mut().authorization_list = Some(authorization_list);
367 }
368}
369
370impl FoundryTransactionBuilder<TempoNetwork> for <TempoNetwork as Network>::TransactionRequest {
371 fn reset_gas_limit(&mut self) {
372 self.gas = None;
373 }
374
375 fn authorization_list(&self) -> Option<&Vec<SignedAuthorization>> {
376 self.authorization_list.as_ref()
377 }
378
379 fn set_authorization_list(&mut self, authorization_list: Vec<SignedAuthorization>) {
380 self.authorization_list = Some(authorization_list);
381 }
382
383 fn fee_token(&self) -> Option<Address> {
384 self.fee_token
385 }
386
387 fn set_fee_token(&mut self, fee_token: Address) {
388 self.fee_token = Some(fee_token);
389 }
390
391 fn nonce_key(&self) -> Option<U256> {
392 self.nonce_key
393 }
394
395 fn set_nonce_key(&mut self, nonce_key: U256) {
396 self.nonce_key = Some(nonce_key);
397 }
398
399 fn key_id(&self) -> Option<Address> {
400 self.key_id
401 }
402
403 fn set_key_id(&mut self, key_id: Address) {
404 self.key_id = Some(key_id);
405 }
406
407 fn valid_before(&self) -> Option<u64> {
408 self.valid_before
409 }
410
411 fn set_valid_before(&mut self, valid_before: u64) {
412 self.valid_before = Some(valid_before);
413 }
414
415 fn valid_after(&self) -> Option<u64> {
416 self.valid_after
417 }
418
419 fn set_valid_after(&mut self, valid_after: u64) {
420 self.valid_after = Some(valid_after);
421 }
422
423 fn fee_payer_signature(&self) -> Option<Signature> {
424 self.fee_payer_signature
425 }
426
427 fn set_fee_payer_signature(&mut self, signature: Signature) {
428 self.fee_payer_signature = Some(signature);
429 }
430
431 fn compute_sponsor_hash(&self, from: Address) -> Option<B256> {
432 let tx = self.clone().build_aa().ok()?;
433 Some(tx.fee_payer_signature_hash(from))
434 }
435
436 fn set_key_authorization(&mut self, key_authorization: SignedKeyAuthorization) {
437 self.key_authorization = Some(key_authorization);
438 }
439
440 fn convert_create_to_call(&mut self) {
441 if self.calls.is_empty() && self.inner.to.is_some_and(|to| to.is_create()) {
442 let input = self.inner.input.input().cloned().unwrap_or_default();
443 let value = self.inner.value.unwrap_or(U256::ZERO);
444 self.calls.push(Call { to: TxKind::Create, value, input });
445 self.inner.input = Default::default();
446 self.inner.value = None;
447 self.inner.to = None;
448 }
449 }
450
451 fn clear_batch_to(&mut self) {
452 if !self.calls.is_empty() {
453 self.inner.to = None;
454 self.inner.value = None;
455 }
456 }
457
458 fn sign_with_access_key(
459 mut self,
460 provider: &impl Provider<TempoNetwork>,
461 signer: &(impl Signer + Sync),
462 wallet_address: Address,
463 key_address: Address,
464 key_authorization: Option<&SignedKeyAuthorization>,
465 ) -> impl Future<Output = Result<Vec<u8>>> + Send {
466 let auth = key_authorization.cloned();
467 let provisioning_fut = provider.get_keychain_key(wallet_address, key_address);
468
469 async move {
470 if let Some(auth) = auth {
471 let is_provisioned =
472 provisioning_fut.await.map(|info| info.keyId != Address::ZERO).unwrap_or(false);
473
474 if !is_provisioned {
475 self.set_key_authorization(auth);
476 }
477 }
478
479 let tempo_tx = self
480 .build_aa()
481 .map_err(|e| eyre::eyre!("failed to build Tempo AA transaction: {e}"))?;
482
483 let sig_hash = tempo_tx.signature_hash();
484 let signing_hash = KeychainSignature::signing_hash(sig_hash, wallet_address);
485 let raw_sig = signer.sign_hash(&signing_hash).await?;
486
487 let keychain_sig =
488 KeychainSignature::new(wallet_address, PrimitiveSignature::Secp256k1(raw_sig));
489 let aa_signed = tempo_tx.into_signed(TempoSignature::Keychain(keychain_sig));
490
491 let mut buf = Vec::new();
492 aa_signed.encode_2718(&mut buf);
493 Ok(buf)
494 }
495 }
496}