foundry_evm_core/evm/
block_context.rs1use alloy_consensus::BlockHeader;
2use alloy_evm::FromRecoveredTx;
3use alloy_network::{BlockResponse, TransactionResponse};
4use alloy_provider::Provider;
5use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions};
6use eyre::{Result, WrapErr};
7use foundry_evm_networks::NetworkConfigs;
8
9use super::{BlockResponseFor, ChainFor, FoundryEvmNetwork, TxEnvFor};
10use crate::FoundryChain;
11
12#[derive(Clone, Debug)]
14pub struct BlockContext<FEN: FoundryEvmNetwork> {
15 grandparent: Vec<TxEnvFor<FEN>>,
16 parent: Vec<TxEnvFor<FEN>>,
17 current: Vec<TxEnvFor<FEN>>,
18}
19
20impl<FEN: FoundryEvmNetwork> BlockContext<FEN> {
21 pub const fn new(
23 grandparent: Vec<TxEnvFor<FEN>>,
24 parent: Vec<TxEnvFor<FEN>>,
25 current: Vec<TxEnvFor<FEN>>,
26 ) -> Self {
27 Self { grandparent, parent, current }
28 }
29
30 pub async fn fetch<P: Provider<FEN::Network>>(
32 provider: &P,
33 block: &BlockResponseFor<FEN>,
34 ) -> Result<Self> {
35 let current = transaction_envs::<FEN>(block)?;
36 let parent = fetch_parent::<FEN, P>(provider, block).await?;
37 let grandparent = if let Some(parent) = &parent {
38 fetch_parent::<FEN, P>(provider, parent).await?
39 } else {
40 None
41 };
42
43 Ok(Self::new(
44 grandparent.as_ref().map(transaction_envs::<FEN>).transpose()?.unwrap_or_default(),
45 parent.as_ref().map(transaction_envs::<FEN>).transpose()?.unwrap_or_default(),
46 current,
47 ))
48 }
49
50 pub fn transaction(&self, index: usize) -> ChainFor<FEN> {
52 ChainFor::<FEN>::for_block(&self.grandparent, &self.parent, &self.current, index)
53 }
54
55 pub fn before_transaction(mut self, index: usize) -> Result<Self> {
57 if index > self.current.len() {
58 eyre::bail!(
59 "transaction index {index} exceeds block transaction count {}",
60 self.current.len()
61 );
62 }
63 self.current.truncate(index);
64 Ok(self)
65 }
66
67 pub fn into_child(mut self) -> Self {
69 self.grandparent = std::mem::take(&mut self.parent);
70 self.parent = std::mem::take(&mut self.current);
71 self
72 }
73
74 pub fn next_transaction(&self, tx: &TxEnvFor<FEN>) -> ChainFor<FEN> {
76 let mut current = self.current.clone();
77 let index = current.len();
78 current.push(tx.clone());
79 ChainFor::<FEN>::for_block(&self.grandparent, &self.parent, ¤t, index)
80 }
81
82 pub fn record_transaction(&mut self, tx: TxEnvFor<FEN>) {
84 self.current.push(tx);
85 }
86
87 pub fn advance_block(&mut self) {
89 self.grandparent = std::mem::take(&mut self.parent);
90 self.parent = std::mem::take(&mut self.current);
91 }
92}
93
94pub async fn context_for_child_transaction<FEN, P>(
96 provider: &P,
97 block_number: u64,
98 tx: &TxEnvFor<FEN>,
99 networks: NetworkConfigs,
100) -> Result<ChainFor<FEN>>
101where
102 FEN: FoundryEvmNetwork,
103 P: Provider<FEN::Network>,
104{
105 if !networks.is_monad() {
106 return Ok(ChainFor::<FEN>::for_transaction(tx));
107 }
108
109 let block = provider
110 .get_block(BlockNumberOrTag::Number(block_number).into())
111 .full()
112 .await?
113 .ok_or_else(|| eyre::eyre!("block {block_number} not found while building EVM context"))?;
114 let parent = fetch_parent::<FEN, P>(provider, &block).await?;
115 let current = transaction_envs::<FEN>(&block)?;
116 let parent = parent.as_ref().map(transaction_envs::<FEN>).transpose()?.unwrap_or_default();
117
118 Ok(BlockContext::<FEN>::new(Vec::new(), parent, current).into_child().next_transaction(tx))
119}
120
121async fn fetch_parent<FEN, P>(
122 provider: &P,
123 block: &BlockResponseFor<FEN>,
124) -> Result<Option<BlockResponseFor<FEN>>>
125where
126 FEN: FoundryEvmNetwork,
127 P: Provider<FEN::Network>,
128{
129 let parent_hash = block.header().parent_hash();
130 if parent_hash.is_zero() {
131 return Ok(None);
132 }
133
134 provider
135 .get_block_by_hash(parent_hash)
136 .full()
137 .await
138 .wrap_err_with(|| format!("failed to fetch ancestor block {parent_hash}"))?
139 .map(Some)
140 .ok_or_else(|| eyre::eyre!("ancestor block {parent_hash} not found"))
141}
142
143fn transaction_envs<FEN: FoundryEvmNetwork>(
144 block: &BlockResponseFor<FEN>,
145) -> Result<Vec<TxEnvFor<FEN>>> {
146 let BlockTransactions::Full(transactions) = block.transactions() else {
147 eyre::bail!("block {} does not contain full transactions", block.header().number());
148 };
149 Ok(transactions
150 .iter()
151 .map(|tx| TxEnvFor::<FEN>::from_recovered_tx(tx.as_ref(), tx.from()))
152 .collect())
153}