1use std::fmt::Debug;
2
3use alloy_consensus::Typed2718;
4pub use alloy_evm::EvmEnv;
5use alloy_evm::FromRecoveredTx;
6use alloy_network::{AnyRpcTransaction, AnyTxEnvelope, TransactionResponse};
7use alloy_primitives::{Address, B256, Bytes, U256};
8#[cfg(feature = "optimism")]
9use op_revm::transaction::deposit::DEPOSIT_TRANSACTION_TYPE;
10use revm::{
11 Context, Database, Journal,
12 context::{Block, BlockEnv, Cfg, CfgEnv, Transaction, TxEnv},
13 context_interface::{
14 ContextTr,
15 either::Either,
16 transaction::{AccessList, RecoveredAuthorization, SignedAuthorization},
17 },
18 inspector::JournalExt,
19 primitives::{TxKind, hardfork::SpecId},
20};
21use tempo_revm::{TempoBlockEnv, TempoTxEnv};
22
23use crate::backend::JournaledState;
24
25pub trait FoundryBlock: Block {
27 fn set_number(&mut self, number: U256);
29
30 fn set_beneficiary(&mut self, beneficiary: Address);
32
33 fn set_timestamp(&mut self, timestamp: U256);
35
36 fn set_gas_limit(&mut self, gas_limit: u64);
38
39 fn set_basefee(&mut self, basefee: u64);
41
42 fn set_difficulty(&mut self, difficulty: U256);
44
45 fn set_prevrandao(&mut self, prevrandao: Option<B256>);
47
48 fn set_blob_excess_gas_and_price(
50 &mut self,
51 _excess_blob_gas: u64,
52 _base_fee_update_fraction: u64,
53 );
54
55 fn timestamp_millis_part(&self) -> u64 {
59 0
60 }
61
62 fn set_timestamp_millis_part(&mut self, _millis: u64) {}
64}
65
66impl FoundryBlock for BlockEnv {
67 fn set_number(&mut self, number: U256) {
68 self.number = number;
69 }
70
71 fn set_beneficiary(&mut self, beneficiary: Address) {
72 self.beneficiary = beneficiary;
73 }
74
75 fn set_timestamp(&mut self, timestamp: U256) {
76 self.timestamp = timestamp;
77 }
78
79 fn set_gas_limit(&mut self, gas_limit: u64) {
80 self.gas_limit = gas_limit;
81 }
82
83 fn set_basefee(&mut self, basefee: u64) {
84 self.basefee = basefee;
85 }
86
87 fn set_difficulty(&mut self, difficulty: U256) {
88 self.difficulty = difficulty;
89 }
90
91 fn set_prevrandao(&mut self, prevrandao: Option<B256>) {
92 self.prevrandao = prevrandao;
93 }
94
95 fn set_blob_excess_gas_and_price(
96 &mut self,
97 excess_blob_gas: u64,
98 base_fee_update_fraction: u64,
99 ) {
100 self.set_blob_excess_gas_and_price(excess_blob_gas, base_fee_update_fraction);
101 }
102}
103
104impl FoundryBlock for TempoBlockEnv {
105 fn set_number(&mut self, number: U256) {
106 self.inner.set_number(number);
107 }
108
109 fn set_beneficiary(&mut self, beneficiary: Address) {
110 self.inner.set_beneficiary(beneficiary);
111 }
112
113 fn set_timestamp(&mut self, timestamp: U256) {
114 self.inner.set_timestamp(timestamp);
115 }
116
117 fn set_gas_limit(&mut self, gas_limit: u64) {
118 self.inner.set_gas_limit(gas_limit);
119 }
120
121 fn set_basefee(&mut self, basefee: u64) {
122 self.inner.set_basefee(basefee);
123 }
124
125 fn set_difficulty(&mut self, difficulty: U256) {
126 self.inner.set_difficulty(difficulty);
127 }
128
129 fn set_prevrandao(&mut self, prevrandao: Option<B256>) {
130 self.inner.set_prevrandao(prevrandao);
131 }
132
133 fn set_blob_excess_gas_and_price(
134 &mut self,
135 _excess_blob_gas: u64,
136 _base_fee_update_fraction: u64,
137 ) {
138 }
139
140 fn timestamp_millis_part(&self) -> u64 {
141 self.timestamp_millis_part
142 }
143
144 fn set_timestamp_millis_part(&mut self, millis: u64) {
145 self.timestamp_millis_part = millis;
146 }
147}
148
149pub trait FoundryTransaction: Transaction {
152 fn set_tx_type(&mut self, tx_type: u8);
154
155 fn set_caller(&mut self, caller: Address);
157
158 fn set_gas_limit(&mut self, gas_limit: u64);
160
161 fn set_gas_price(&mut self, gas_price: u128);
163
164 fn set_kind(&mut self, kind: TxKind);
166
167 fn set_value(&mut self, value: U256);
169
170 fn set_data(&mut self, data: Bytes);
172
173 fn set_nonce(&mut self, nonce: u64);
175
176 fn set_chain_id(&mut self, chain_id: Option<u64>);
178
179 fn set_access_list(&mut self, access_list: AccessList);
181
182 fn authorization_list_mut(
184 &mut self,
185 ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>>;
186
187 fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>);
189
190 fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>);
192
193 fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128);
195
196 fn set_signed_authorization(&mut self, auth: Vec<SignedAuthorization>) {
198 *self.authorization_list_mut() = auth.into_iter().map(Either::Left).collect();
199 }
200
201 fn enveloped_tx(&self) -> Option<&Bytes> {
205 None
206 }
207
208 fn set_enveloped_tx(&mut self, _bytes: Bytes) {}
210
211 fn source_hash(&self) -> Option<B256> {
213 None
214 }
215
216 fn set_source_hash(&mut self, _source_hash: B256) {}
218
219 fn mint(&self) -> Option<u128> {
221 None
222 }
223
224 fn set_mint(&mut self, _mint: u128) {}
226
227 fn is_system_transaction(&self) -> bool {
229 false
230 }
231
232 fn set_system_transaction(&mut self, _is_system_transaction: bool) {}
234
235 fn is_deposit(&self) -> bool {
237 #[cfg(feature = "optimism")]
238 {
239 self.tx_type() == DEPOSIT_TRANSACTION_TYPE
240 }
241 #[cfg(not(feature = "optimism"))]
242 {
243 false
244 }
245 }
246
247 fn fee_token(&self) -> Option<Address> {
251 None
252 }
253
254 fn set_fee_token(&mut self, _token: Option<Address>) {}
256
257 fn fee_payer(&self) -> Option<Option<Address>> {
259 None
260 }
261
262 fn set_fee_payer(&mut self, _payer: Option<Option<Address>>) {}
264}
265
266impl FoundryTransaction for TxEnv {
267 fn set_tx_type(&mut self, tx_type: u8) {
268 self.tx_type = tx_type;
269 }
270
271 fn set_caller(&mut self, caller: Address) {
272 self.caller = caller;
273 }
274
275 fn set_gas_limit(&mut self, gas_limit: u64) {
276 self.gas_limit = gas_limit;
277 }
278
279 fn set_gas_price(&mut self, gas_price: u128) {
280 self.gas_price = gas_price;
281 }
282
283 fn set_kind(&mut self, kind: TxKind) {
284 self.kind = kind;
285 }
286
287 fn set_value(&mut self, value: U256) {
288 self.value = value;
289 }
290
291 fn set_data(&mut self, data: Bytes) {
292 self.data = data;
293 }
294
295 fn set_nonce(&mut self, nonce: u64) {
296 self.nonce = nonce;
297 }
298
299 fn set_chain_id(&mut self, chain_id: Option<u64>) {
300 self.chain_id = chain_id;
301 }
302
303 fn set_access_list(&mut self, access_list: AccessList) {
304 self.access_list = access_list;
305 }
306
307 fn authorization_list_mut(
308 &mut self,
309 ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
310 &mut self.authorization_list
311 }
312
313 fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
314 self.gas_priority_fee = gas_priority_fee;
315 }
316
317 fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>) {
318 self.blob_hashes = blob_hashes;
319 }
320
321 fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
322 self.max_fee_per_blob_gas = max_fee_per_blob_gas;
323 }
324}
325
326impl FoundryTransaction for TempoTxEnv {
327 fn set_tx_type(&mut self, tx_type: u8) {
328 self.inner.set_tx_type(tx_type);
329 }
330
331 fn set_caller(&mut self, caller: Address) {
332 self.inner.set_caller(caller);
333 }
334
335 fn set_gas_limit(&mut self, gas_limit: u64) {
336 self.inner.set_gas_limit(gas_limit);
337 }
338
339 fn set_gas_price(&mut self, gas_price: u128) {
340 self.inner.set_gas_price(gas_price);
341 }
342
343 fn set_kind(&mut self, kind: TxKind) {
344 self.inner.set_kind(kind);
345 if let Some(call) =
346 self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
347 {
348 call.to = kind;
349 }
350 }
351
352 fn set_value(&mut self, value: U256) {
353 self.inner.set_value(value);
354 if let Some(call) =
355 self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
356 {
357 call.value = value;
358 }
359 }
360
361 fn set_data(&mut self, data: Bytes) {
362 self.inner.set_data(data.clone());
363 if let Some(call) =
364 self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
365 {
366 call.input = data;
367 }
368 }
369
370 fn set_nonce(&mut self, nonce: u64) {
371 self.inner.set_nonce(nonce);
372 }
373
374 fn set_chain_id(&mut self, chain_id: Option<u64>) {
375 self.inner.set_chain_id(chain_id);
376 }
377
378 fn set_access_list(&mut self, access_list: AccessList) {
379 self.inner.set_access_list(access_list);
380 }
381
382 fn authorization_list_mut(
383 &mut self,
384 ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
385 self.inner.authorization_list_mut()
386 }
387
388 fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
389 self.inner.set_gas_priority_fee(gas_priority_fee);
390 }
391
392 fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
393
394 fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
395
396 fn fee_token(&self) -> Option<Address> {
397 self.fee_token
398 }
399
400 fn set_fee_token(&mut self, token: Option<Address>) {
401 self.fee_token = token;
402 }
403
404 fn fee_payer(&self) -> Option<Option<Address>> {
405 self.fee_payer
406 }
407
408 fn set_fee_payer(&mut self, payer: Option<Option<Address>>) {
409 self.fee_payer = payer;
410 }
411}
412
413pub trait FoundryChain<Tx>: Clone + Debug + Default + Send + Sync {
417 fn for_transaction(_tx: &Tx) -> Self {
419 Self::default()
420 }
421
422 fn for_block(
424 _grandparent: &[Tx],
425 _parent: &[Tx],
426 _current: &[Tx],
427 _current_tx_index: usize,
428 ) -> Self {
429 Self::default()
430 }
431
432 fn refresh_journal<J: FoundryJournal>(&self, _journal: &mut J) {}
434}
435
436impl<Tx> FoundryChain<Tx> for () {}
437
438pub trait FoundryJournal: JournalExt {
440 #[cfg(feature = "monad")]
442 fn capture_reserve_balance(
443 &self,
444 ) -> monad_revm::reserve_balance::tracker::ReserveBalanceTracker {
445 monad_revm::reserve_balance::tracker::ReserveBalanceTracker::default()
446 }
447
448 #[cfg(feature = "monad")]
450 fn restore_reserve_balance(
451 &mut self,
452 _tracker: monad_revm::reserve_balance::tracker::ReserveBalanceTracker,
453 ) {
454 }
455
456 #[cfg(feature = "monad")]
460 fn preserves_reserve_balance(&self) -> bool {
461 false
462 }
463
464 #[cfg(feature = "monad")]
466 fn set_preserve_reserve_balance(&mut self, _preserve: bool) {}
467}
468
469impl<DB: Database> FoundryJournal for Journal<DB> {}
470
471#[cfg(feature = "monad")]
472impl<DB: Database> FoundryJournal for monad_revm::MonadJournal<DB> {
473 fn capture_reserve_balance(
474 &self,
475 ) -> monad_revm::reserve_balance::tracker::ReserveBalanceTracker {
476 monad_revm::MonadJournalTr::reserve_balance(self).clone()
477 }
478
479 fn restore_reserve_balance(
480 &mut self,
481 tracker: monad_revm::reserve_balance::tracker::ReserveBalanceTracker,
482 ) {
483 *monad_revm::MonadJournalTr::reserve_balance_mut(self) = tracker;
484 }
485
486 fn preserves_reserve_balance(&self) -> bool {
487 monad_revm::MonadJournalTr::preserves_reserve_balance_tracker(self)
488 }
489
490 fn set_preserve_reserve_balance(&mut self, preserve: bool) {
491 monad_revm::MonadJournalTr::set_preserve_reserve_balance_tracker(self, preserve);
492 }
493}
494
495pub trait FoundryContextExt:
500 ContextTr<
501 Block: FoundryBlock + Clone,
502 Tx: FoundryTransaction + Clone,
503 Cfg: Cfg<Spec = Self::Spec> + Clone + From<CfgEnv<Self::Spec>> + Into<CfgEnv<Self::Spec>>,
504 Journal: FoundryJournal,
505 Chain: FoundryChain<Self::Tx>,
506 >
507{
508 type Spec: Into<SpecId> + Copy + Debug;
512
513 fn block_mut(&mut self) -> &mut Self::Block;
515
516 fn tx_mut(&mut self) -> &mut Self::Tx;
518
519 fn cfg_mut(&mut self) -> &mut Self::Cfg;
521
522 fn cfg_env(&self) -> &CfgEnv<Self::Spec>;
524
525 fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec>;
527
528 fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState);
530
531 fn journal_inner(&self) -> &JournaledState;
533
534 fn set_spec_and_gas_params(&mut self, spec: Self::Spec) {
536 self.cfg_env_mut().set_spec_and_mainnet_gas_params(spec);
537 }
538
539 fn set_block(&mut self, block: Self::Block) {
541 *self.block_mut() = block;
542 }
543
544 fn set_tx(&mut self, tx: Self::Tx) {
546 *self.tx_mut() = tx;
547 }
548
549 fn set_cfg(&mut self, cfg: Self::Cfg) {
551 *self.cfg_mut() = cfg;
552 }
553
554 fn set_journal_inner(&mut self, journal_inner: JournaledState) {
556 *self.db_journal_inner_mut().1 = journal_inner;
557 }
558
559 fn set_evm(&mut self, evm_env: EvmEnv<Self::Spec, Self::Block>) {
561 *self.cfg_mut() = evm_env.cfg_env.into();
562 *self.block_mut() = evm_env.block_env;
563 }
564
565 fn tx_clone(&self) -> Self::Tx {
567 self.tx().clone()
568 }
569
570 fn evm_clone(&self) -> EvmEnv<Self::Spec, Self::Block> {
572 EvmEnv::new(self.cfg().clone().into(), self.block().clone())
573 }
574}
575
576pub fn refresh_chain_journal<CTX: FoundryContextExt>(context: &mut CTX) {
578 let chain = context.chain().clone();
579 chain.refresh_journal(context.journal_mut());
580}
581
582impl<
583 BLOCK: FoundryBlock + Clone,
584 TX: FoundryTransaction + Clone,
585 SPEC: Into<SpecId> + Copy + Debug,
586 DB: Database,
587 C: FoundryChain<TX>,
588> FoundryContextExt for Context<BLOCK, TX, CfgEnv<SPEC>, DB, Journal<DB>, C>
589{
590 type Spec = <Self::Cfg as Cfg>::Spec;
591 fn block_mut(&mut self) -> &mut Self::Block {
592 &mut self.block
593 }
594
595 fn tx_mut(&mut self) -> &mut Self::Tx {
596 &mut self.tx
597 }
598
599 fn cfg_mut(&mut self) -> &mut Self::Cfg {
600 &mut self.cfg
601 }
602
603 fn cfg_env(&self) -> &CfgEnv<Self::Spec> {
604 &self.cfg
605 }
606
607 fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec> {
608 &mut self.cfg
609 }
610
611 fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState) {
612 (&mut self.journaled_state.database, &mut self.journaled_state.inner)
613 }
614
615 fn journal_inner(&self) -> &JournaledState {
616 &self.journaled_state.inner
617 }
618}
619
620#[cfg(feature = "monad")]
621impl<DB: Database> FoundryContextExt
622 for Context<
623 BlockEnv,
624 TxEnv,
625 monad_revm::MonadCfgEnv,
626 DB,
627 monad_revm::MonadJournal<DB>,
628 monad_revm::MonadChainContext,
629 >
630{
631 type Spec = <Self::Cfg as Cfg>::Spec;
632 fn block_mut(&mut self) -> &mut Self::Block {
633 &mut self.block
634 }
635
636 fn tx_mut(&mut self) -> &mut Self::Tx {
637 &mut self.tx
638 }
639
640 fn cfg_mut(&mut self) -> &mut Self::Cfg {
641 &mut self.cfg
642 }
643
644 fn cfg_env(&self) -> &CfgEnv<Self::Spec> {
645 self.cfg.inner()
646 }
647
648 fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec> {
649 self.cfg.inner_mut()
650 }
651
652 fn set_spec_and_gas_params(&mut self, spec: Self::Spec) {
653 let mut cfg = self.cfg.clone().into_inner();
654 cfg.spec = spec;
655 self.cfg = monad_revm::MonadCfgEnv::from(cfg);
656 }
657
658 fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState) {
659 let journal: &mut Journal<DB> = std::ops::DerefMut::deref_mut(&mut self.journaled_state);
660 (&mut journal.database, &mut journal.inner)
661 }
662
663 fn journal_inner(&self) -> &JournaledState {
664 let journal: &Journal<DB> = std::ops::Deref::deref(&self.journaled_state);
665 &journal.inner
666 }
667}
668
669pub trait FromAnyRpcTransaction: Sized {
675 fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self>;
677}
678
679impl FromAnyRpcTransaction for TxEnv {
680 fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
681 if let Some(envelope) = tx.as_envelope() {
682 Ok(Self::from_recovered_tx(envelope, tx.from()))
683 } else {
684 eyre::bail!("cannot convert unknown transaction type to TxEnv");
685 }
686 }
687}
688
689impl FromAnyRpcTransaction for TempoTxEnv {
690 fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
691 use alloy_consensus::Transaction as _;
692 if let Some(envelope) = tx.as_envelope() {
693 return Ok(TxEnv::from_recovered_tx(envelope, tx.from()).into());
694 }
695
696 if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner
698 && unknown.ty() == tempo_alloy::primitives::TEMPO_TX_TYPE_ID
699 {
700 let base = TxEnv {
701 tx_type: unknown.ty(),
702 caller: tx.from(),
703 gas_limit: unknown.gas_limit(),
704 gas_price: unknown.max_fee_per_gas(),
705 gas_priority_fee: unknown.max_priority_fee_per_gas(),
706 kind: unknown.kind(),
707 value: unknown.value(),
708 data: unknown.input().clone(),
709 nonce: unknown.nonce(),
710 chain_id: unknown.chain_id(),
711 access_list: unknown.access_list().cloned().unwrap_or_default(),
712 ..Default::default()
713 };
714 let fee_token =
715 unknown.inner.fields.get_deserialized::<Address>("feeToken").and_then(Result::ok);
716 return Ok(Self { inner: base, fee_token, ..Default::default() });
717 }
718
719 eyre::bail!("cannot convert unknown transaction type to TempoTxEnv");
720 }
721}
722
723#[cfg(feature = "optimism")]
724mod optimism {
725 use super::*;
726 use alloy_op_evm::OpTx;
727 use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, TxDeposit};
728 use op_revm::{OpTransaction, transaction::OpTxTr};
729
730 impl<TX: FoundryTransaction> FoundryTransaction for OpTransaction<TX> {
731 fn set_tx_type(&mut self, tx_type: u8) {
732 self.base.set_tx_type(tx_type);
733 }
734
735 fn set_caller(&mut self, caller: Address) {
736 self.base.set_caller(caller);
737 }
738
739 fn set_gas_limit(&mut self, gas_limit: u64) {
740 self.base.set_gas_limit(gas_limit);
741 }
742
743 fn set_gas_price(&mut self, gas_price: u128) {
744 self.base.set_gas_price(gas_price);
745 }
746
747 fn set_kind(&mut self, kind: TxKind) {
748 self.base.set_kind(kind);
749 }
750
751 fn set_value(&mut self, value: U256) {
752 self.base.set_value(value);
753 }
754
755 fn set_data(&mut self, data: Bytes) {
756 self.base.set_data(data);
757 }
758
759 fn set_nonce(&mut self, nonce: u64) {
760 self.base.set_nonce(nonce);
761 }
762
763 fn set_chain_id(&mut self, chain_id: Option<u64>) {
764 self.base.set_chain_id(chain_id);
765 }
766
767 fn set_access_list(&mut self, access_list: AccessList) {
768 self.base.set_access_list(access_list);
769 }
770
771 fn authorization_list_mut(
772 &mut self,
773 ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
774 self.base.authorization_list_mut()
775 }
776
777 fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
778 self.base.set_gas_priority_fee(gas_priority_fee);
779 }
780
781 fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
782
783 fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
784
785 fn enveloped_tx(&self) -> Option<&Bytes> {
786 OpTxTr::enveloped_tx(self)
787 }
788
789 fn set_enveloped_tx(&mut self, bytes: Bytes) {
790 self.enveloped_tx = Some(bytes);
791 }
792
793 fn source_hash(&self) -> Option<B256> {
794 OpTxTr::source_hash(self)
795 }
796
797 fn set_source_hash(&mut self, source_hash: B256) {
798 if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
799 self.deposit.source_hash = source_hash;
800 }
801 }
802
803 fn mint(&self) -> Option<u128> {
804 OpTxTr::mint(self)
805 }
806
807 fn set_mint(&mut self, mint: u128) {
808 if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
809 self.deposit.mint = Some(mint);
810 }
811 }
812
813 fn is_system_transaction(&self) -> bool {
814 OpTxTr::is_system_transaction(self)
815 }
816
817 fn set_system_transaction(&mut self, is_system_transaction: bool) {
818 if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
819 self.deposit.is_system_transaction = is_system_transaction;
820 }
821 }
822 }
823
824 impl FoundryTransaction for OpTx {
825 fn set_tx_type(&mut self, tx_type: u8) {
826 self.0.set_tx_type(tx_type);
827 }
828
829 fn set_caller(&mut self, caller: Address) {
830 self.0.set_caller(caller);
831 }
832
833 fn set_gas_limit(&mut self, gas_limit: u64) {
834 self.0.set_gas_limit(gas_limit);
835 }
836
837 fn set_gas_price(&mut self, gas_price: u128) {
838 self.0.set_gas_price(gas_price);
839 }
840
841 fn set_kind(&mut self, kind: TxKind) {
842 self.0.set_kind(kind);
843 }
844
845 fn set_value(&mut self, value: U256) {
846 self.0.set_value(value);
847 }
848
849 fn set_data(&mut self, data: Bytes) {
850 self.0.set_data(data);
851 }
852
853 fn set_nonce(&mut self, nonce: u64) {
854 self.0.set_nonce(nonce);
855 }
856
857 fn set_chain_id(&mut self, chain_id: Option<u64>) {
858 self.0.set_chain_id(chain_id);
859 }
860
861 fn set_access_list(&mut self, access_list: AccessList) {
862 self.0.set_access_list(access_list);
863 }
864
865 fn authorization_list_mut(
866 &mut self,
867 ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
868 self.0.authorization_list_mut()
869 }
870
871 fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
872 self.0.set_gas_priority_fee(gas_priority_fee);
873 }
874
875 fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
876
877 fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
878
879 fn enveloped_tx(&self) -> Option<&Bytes> {
880 FoundryTransaction::enveloped_tx(&self.0)
881 }
882
883 fn set_enveloped_tx(&mut self, bytes: Bytes) {
884 self.0.set_enveloped_tx(bytes);
885 }
886
887 fn source_hash(&self) -> Option<B256> {
888 FoundryTransaction::source_hash(&self.0)
889 }
890
891 fn set_source_hash(&mut self, source_hash: B256) {
892 self.0.set_source_hash(source_hash);
893 }
894
895 fn mint(&self) -> Option<u128> {
896 FoundryTransaction::mint(&self.0)
897 }
898
899 fn set_mint(&mut self, mint: u128) {
900 self.0.set_mint(mint);
901 }
902
903 fn is_system_transaction(&self) -> bool {
904 FoundryTransaction::is_system_transaction(&self.0)
905 }
906
907 fn set_system_transaction(&mut self, is_system_transaction: bool) {
908 self.0.set_system_transaction(is_system_transaction);
909 }
910 }
911
912 impl FromAnyRpcTransaction for OpTx {
913 fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
914 if let Some(envelope) = tx.as_envelope() {
915 return Ok(Self(OpTransaction::<TxEnv> {
916 base: TxEnv::from_recovered_tx(envelope, tx.from()),
917 enveloped_tx: None,
918 deposit: Default::default(),
919 }));
920 }
921
922 if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner
924 && unknown.ty() == DEPOSIT_TX_TYPE_ID
925 {
926 let mut fields = unknown.inner.fields.clone();
927 fields.insert("from".to_string(), serde_json::to_value(tx.from())?);
928 let deposit_tx: TxDeposit = fields
929 .deserialize_into()
930 .map_err(|e| eyre::eyre!("failed to deserialize deposit tx: {e}"))?;
931 return Ok(Self::from_recovered_tx(&deposit_tx, deposit_tx.from));
932 }
933
934 eyre::bail!("cannot convert unknown transaction type to OpTransaction");
935 }
936 }
937}
938
939#[cfg(test)]
940mod tests {
941 use std::num::NonZeroU64;
942
943 use super::*;
944 use alloy_consensus::{Signed, TxEip1559, transaction::Recovered};
945 use alloy_evm::{EthEvmFactory, EvmFactory};
946 use alloy_network::{AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction};
947 use alloy_primitives::Signature;
948 use alloy_rpc_types::{Transaction as RpcTransaction, TransactionInfo};
949 use alloy_serde::WithOtherFields;
950 use foundry_evm_hardforks::TempoHardfork;
951 use revm::database::EmptyDB;
952 use tempo_alloy::primitives::{
953 AASigned, TempoSignature, TempoTransaction, TempoTxEnvelope,
954 transaction::{Call, PrimitiveSignature},
955 };
956 use tempo_evm::TempoEvmFactory;
957
958 #[test]
959 fn eth_evm_foundry_context_ext_implementation() {
960 let mut evm = EthEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
961
962 evm.ctx_mut().block_mut().set_number(U256::from(123));
964 assert_eq!(evm.ctx().block().number(), U256::from(123));
965
966 evm.ctx_mut().tx_mut().set_nonce(99);
968 assert_eq!(evm.ctx().tx().nonce(), 99);
969
970 evm.ctx_mut().cfg_mut().spec = SpecId::AMSTERDAM;
972 assert_eq!(evm.ctx().cfg().spec, SpecId::AMSTERDAM);
973
974 let tx_env = evm.ctx().tx_clone();
976 evm.ctx_mut().set_tx(tx_env);
977 let evm_env = evm.ctx().evm_clone();
978 evm.ctx_mut().set_evm(evm_env);
979 }
980
981 #[test]
982 #[cfg(feature = "monad")]
983 fn monad_evm_foundry_context_ext_implementation() {
984 let mut evm = alloy_monad_evm::MonadEvmFactory::default().create_evm(
985 EmptyDB::default(),
986 EvmEnv::new(
987 CfgEnv::new_with_spec(monad_revm::MonadHardfork::MonadNine),
988 BlockEnv::default(),
989 ),
990 );
991
992 evm.ctx_mut().block_mut().set_number(U256::from(123));
994 assert_eq!(evm.ctx().block().number(), U256::from(123));
995
996 evm.ctx_mut().tx_mut().set_nonce(99);
998 assert_eq!(evm.ctx().tx().nonce(), 99);
999
1000 evm.ctx_mut().cfg_mut().spec = monad_revm::MonadHardfork::MonadEight;
1002 assert_eq!(evm.ctx().cfg().spec, monad_revm::MonadHardfork::MonadEight);
1003
1004 let tx_env = evm.ctx().tx_clone();
1006 evm.ctx_mut().set_tx(tx_env);
1007 let evm_env = evm.ctx().evm_clone();
1008 evm.ctx_mut().set_evm(evm_env);
1009 }
1010
1011 #[test]
1012 #[cfg(feature = "monad")]
1013 fn monad_memory_limit_follows_hardfork_transitions() {
1014 const FOUNDRY_MEMORY_LIMIT: u64 = 128 * 1024 * 1024;
1015
1016 let mut cfg = CfgEnv::new_with_spec(monad_revm::MonadHardfork::MonadEight);
1017 cfg.memory_limit = FOUNDRY_MEMORY_LIMIT;
1018 let mut evm = alloy_monad_evm::MonadEvmFactory::default()
1019 .create_evm(EmptyDB::default(), EvmEnv::new(cfg, BlockEnv::default()));
1020
1021 assert_eq!(evm.ctx().cfg().memory_limit(), FOUNDRY_MEMORY_LIMIT);
1022
1023 evm.ctx_mut().set_spec_and_gas_params(monad_revm::MonadHardfork::MonadNine);
1024 assert_eq!(evm.ctx().cfg().inner().memory_limit, FOUNDRY_MEMORY_LIMIT);
1025 assert_eq!(evm.ctx().cfg().memory_limit(), monad_revm::cfg::MONAD_MEMORY_LIMIT);
1026
1027 evm.ctx_mut().set_spec_and_gas_params(monad_revm::MonadHardfork::MonadEight);
1028 assert_eq!(evm.ctx().cfg().memory_limit(), FOUNDRY_MEMORY_LIMIT);
1029 }
1030
1031 #[test]
1032 fn tempo_evm_foundry_context_ext_implementation() {
1033 let mut evm = TempoEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
1034
1035 evm.ctx_mut().block_mut().set_number(U256::from(123));
1037 assert_eq!(evm.ctx().block().number(), U256::from(123));
1038
1039 evm.ctx_mut().tx_mut().set_nonce(99);
1041 assert_eq!(evm.ctx().tx().nonce(), 99);
1042
1043 evm.ctx_mut().cfg_mut().spec = TempoHardfork::Genesis;
1045 assert_eq!(evm.ctx().cfg().spec, TempoHardfork::Genesis);
1046
1047 let tx_env = evm.ctx().tx_clone();
1049 evm.ctx_mut().set_tx(tx_env);
1050 let evm_env = evm.ctx().evm_clone();
1051 evm.ctx_mut().set_evm(evm_env);
1052 }
1053
1054 #[test]
1055 fn tempo_tx_env_setters_update_aa_call_payload() {
1056 let old_to = TxKind::Call(Address::with_last_byte(0xAA));
1057 let new_to = TxKind::Create;
1058 let new_value = U256::from(123);
1059 let new_input = Bytes::from_static(b"local bytecode");
1060
1061 let mut tx_env = TempoTxEnv {
1062 inner: TxEnv {
1063 kind: old_to,
1064 value: U256::from(1),
1065 data: Bytes::from_static(b"original bytecode"),
1066 ..Default::default()
1067 },
1068 tempo_tx_env: Some(Box::new(tempo_revm::TempoBatchCallEnv {
1069 aa_calls: vec![Call {
1070 to: old_to,
1071 value: U256::from(1),
1072 input: Bytes::from_static(b"original bytecode"),
1073 }],
1074 ..Default::default()
1075 })),
1076 ..Default::default()
1077 };
1078
1079 tx_env.set_kind(new_to);
1080 tx_env.set_value(new_value);
1081 tx_env.set_data(new_input.clone());
1082
1083 assert_eq!(tx_env.inner.kind, new_to);
1084 assert_eq!(tx_env.inner.value, new_value);
1085 assert_eq!(tx_env.inner.data, new_input);
1086
1087 let call = &tx_env.tempo_tx_env.as_ref().unwrap().aa_calls[0];
1088 assert_eq!(call.to, new_to);
1089 assert_eq!(call.value, new_value);
1090 assert_eq!(call.input, new_input);
1091 }
1092
1093 fn make_signed_eip1559() -> Signed<TxEip1559> {
1094 Signed::new_unchecked(
1095 TxEip1559 {
1096 chain_id: 1,
1097 nonce: 42,
1098 gas_limit: 21001,
1099 to: TxKind::Call(Address::with_last_byte(0xBB)),
1100 value: U256::from(101),
1101 ..Default::default()
1102 },
1103 Signature::new(U256::ZERO, U256::ZERO, false),
1104 B256::ZERO,
1105 )
1106 }
1107
1108 #[test]
1109 fn from_any_rpc_transaction_for_eth() {
1110 let from = Address::random();
1111 let signed_tx = make_signed_eip1559();
1112 let rpc_tx = RpcTransaction::from_transaction(
1113 Recovered::new_unchecked(signed_tx.into(), from),
1114 TransactionInfo::default(),
1115 );
1116
1117 let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1118 let tx_env = TxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1119
1120 assert_eq!(tx_env.caller, from);
1121 assert_eq!(tx_env.nonce, 42);
1122 assert_eq!(tx_env.gas_limit, 21001);
1123 assert_eq!(tx_env.value, U256::from(101));
1124 assert_eq!(tx_env.kind, TxKind::Call(Address::with_last_byte(0xBB)));
1125 }
1126
1127 #[test]
1128 fn from_any_rpc_transaction_unknown_envelope_errors() {
1129 let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
1130 hash: B256::ZERO,
1131 inner: UnknownTypedTransaction {
1132 ty: AnyTxType(0xFF),
1133 fields: Default::default(),
1134 memo: Default::default(),
1135 },
1136 });
1137 let from = Address::random();
1138 let any_tx = AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
1139 inner: Recovered::new_unchecked(unknown, from),
1140 block_hash: None,
1141 block_number: None,
1142 transaction_index: None,
1143 effective_gas_price: None,
1144 block_timestamp: None,
1145 }));
1146
1147 let result = TxEnv::from_any_rpc_transaction(&any_tx).unwrap_err();
1148 assert!(result.to_string().contains("unknown transaction type"));
1149 }
1150
1151 #[test]
1152 fn from_any_rpc_transaction_for_tempo_eth_envelope() {
1153 let from = Address::random();
1154 let signed_tx = make_signed_eip1559();
1155 let rpc_tx = RpcTransaction::from_transaction(
1156 Recovered::new_unchecked(signed_tx.into(), from),
1157 TransactionInfo::default(),
1158 );
1159 let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1160
1161 let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1162 assert_eq!(tx_env.inner.caller, from);
1163 assert_eq!(tx_env.inner.nonce, 42);
1164 assert_eq!(tx_env.inner.gas_limit, 21001);
1165 assert_eq!(tx_env.inner.value, U256::from(101));
1166 assert_eq!(tx_env.fee_token, None);
1167 }
1168
1169 #[test]
1170 fn from_any_rpc_transaction_for_tempo_aa() {
1171 let from = Address::random();
1172 let fee_token = Some(Address::random());
1173 let tempo_tx = TempoTransaction {
1174 chain_id: 42431,
1175 nonce: 42,
1176 gas_limit: 424242,
1177 fee_token,
1178 nonce_key: U256::from(4242),
1179 valid_after: NonZeroU64::new(1800000000),
1180 ..Default::default()
1181 };
1182 let aa_signed = AASigned::new_unhashed(
1183 tempo_tx,
1184 TempoSignature::Primitive(PrimitiveSignature::Secp256k1(Signature::new(
1185 U256::ZERO,
1186 U256::ZERO,
1187 false,
1188 ))),
1189 );
1190
1191 let rpc_tx = RpcTransaction::from_transaction(
1194 Recovered::new_unchecked(TempoTxEnvelope::AA(aa_signed), from),
1195 TransactionInfo::default(),
1196 );
1197 let json = serde_json::to_value(&rpc_tx).unwrap();
1198 let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap();
1199
1200 let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1201 assert_eq!(tx_env.inner.caller, from);
1202 assert_eq!(tx_env.inner.nonce, 42);
1203 assert_eq!(tx_env.inner.gas_limit, 424242);
1204 assert_eq!(tx_env.inner.chain_id, Some(42431));
1205 assert_eq!(tx_env.fee_token, fee_token);
1206 }
1207
1208 #[cfg(feature = "optimism")]
1209 mod optimism {
1210 use super::*;
1211 use alloy_consensus::Sealed;
1212 use alloy_op_evm::{OpEvmFactory, OpTx};
1213 use op_alloy_consensus::{OpTxEnvelope, TxDeposit, transaction::OpTransactionInfo};
1214 use op_alloy_rpc_types::Transaction as OpRpcTransaction;
1215 use op_revm::OpSpecId;
1216
1217 #[test]
1218 fn op_evm_foundry_context_ext_implementation() {
1219 let mut evm =
1220 OpEvmFactory::<OpTx>::default().create_evm(EmptyDB::default(), EvmEnv::default());
1221
1222 evm.ctx_mut().block_mut().set_number(U256::from(123));
1224 assert_eq!(evm.ctx().block().number(), U256::from(123));
1225
1226 evm.ctx_mut().tx_mut().set_nonce(99);
1228 assert_eq!(evm.ctx().tx().nonce(), 99);
1229
1230 evm.ctx_mut().cfg_mut().spec = OpSpecId::JOVIAN;
1232 assert_eq!(evm.ctx().cfg().spec, OpSpecId::JOVIAN);
1233
1234 let tx_env = evm.ctx().tx_clone();
1236 evm.ctx_mut().set_tx(tx_env);
1237 let evm_env = evm.ctx().evm_clone();
1238 evm.ctx_mut().set_evm(evm_env);
1239 }
1240
1241 #[test]
1242 fn from_any_rpc_transaction_for_op() {
1243 let from = Address::random();
1244 let signed_tx = make_signed_eip1559();
1245
1246 let rpc_tx = RpcTransaction::from_transaction(
1248 Recovered::new_unchecked(signed_tx.into(), from),
1249 TransactionInfo::default(),
1250 );
1251 let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1252 let expected_base = TxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1253
1254 let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap();
1255 assert_eq!(op_tx_env.base, expected_base);
1256 }
1257
1258 #[test]
1259 fn from_any_rpc_transaction_for_op_deposit() {
1260 let from = Address::random();
1261 let source_hash = B256::random();
1262 let deposit = TxDeposit {
1263 source_hash,
1264 from,
1265 to: TxKind::Call(Address::with_last_byte(0xCC)),
1266 mint: 1111,
1267 value: U256::from(200),
1268 gas_limit: 21000,
1269 is_system_transaction: true,
1270 input: Default::default(),
1271 };
1272
1273 let op_rpc_tx = OpRpcTransaction::from_transaction(
1276 Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), from),
1277 OpTransactionInfo::default(),
1278 );
1279 let json = serde_json::to_value(&op_rpc_tx).unwrap();
1280 let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap();
1281
1282 let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap();
1283 assert_eq!(op_tx_env.base.caller, from);
1284 assert_eq!(op_tx_env.base.kind, TxKind::Call(Address::with_last_byte(0xCC)));
1285 assert_eq!(op_tx_env.base.value, U256::from(200));
1286 assert_eq!(op_tx_env.base.gas_limit, 21000);
1287 assert_eq!(op_tx_env.deposit.source_hash, source_hash);
1288 assert_eq!(op_tx_env.deposit.mint, Some(1111));
1289 assert!(op_tx_env.deposit.is_system_transaction);
1290 }
1291 }
1292}