alloy_evm/block/
state_changes.rs1use super::{calc, BlockExecutionError};
4use alloy_consensus::BlockHeader;
5use alloy_eips::eip4895::{Withdrawal, Withdrawals};
6use alloy_hardforks::EthereumHardforks;
7use alloy_primitives::{map::HashMap, Address};
8use revm::{
9 context::Block,
10 database::State,
11 state::{Account, AccountStatus, EvmState},
12 Database,
13};
14
15#[inline]
20pub fn post_block_balance_increments<H>(
21 spec: impl EthereumHardforks,
22 block_env: impl Block,
23 ommers: &[H],
24 withdrawals: Option<&Withdrawals>,
25) -> HashMap<Address, u128>
26where
27 H: BlockHeader,
28{
29 let mut balance_increments = HashMap::with_capacity_and_hasher(
30 withdrawals.map_or(ommers.len(), |w| w.len()),
31 Default::default(),
32 );
33
34 if let Some(base_block_reward) =
36 calc::base_block_reward(&spec, block_env.number().saturating_to())
37 {
38 for ommer in ommers {
40 *balance_increments.entry(ommer.beneficiary()).or_default() += calc::ommer_reward(
41 base_block_reward,
42 block_env.number().saturating_to(),
43 ommer.number(),
44 );
45 }
46
47 *balance_increments.entry(block_env.beneficiary()).or_default() +=
49 calc::block_reward(base_block_reward, ommers.len());
50 }
51
52 insert_post_block_withdrawals_balance_increments(
54 spec,
55 block_env.timestamp().saturating_to(),
56 withdrawals.map(|w| w.as_slice()),
57 &mut balance_increments,
58 );
59
60 balance_increments
61}
62
63#[inline]
68pub fn post_block_withdrawals_balance_increments(
69 spec: impl EthereumHardforks,
70 block_timestamp: u64,
71 withdrawals: &[Withdrawal],
72) -> HashMap<Address, u128> {
73 let mut balance_increments =
74 HashMap::with_capacity_and_hasher(withdrawals.len(), Default::default());
75 insert_post_block_withdrawals_balance_increments(
76 spec,
77 block_timestamp,
78 Some(withdrawals),
79 &mut balance_increments,
80 );
81 balance_increments
82}
83
84#[inline]
89pub fn insert_post_block_withdrawals_balance_increments(
90 spec: impl EthereumHardforks,
91 block_timestamp: u64,
92 withdrawals: Option<&[Withdrawal]>,
93 balance_increments: &mut HashMap<Address, u128>,
94) {
95 if spec.is_shanghai_active_at_timestamp(block_timestamp) {
97 if let Some(withdrawals) = withdrawals {
98 for withdrawal in withdrawals {
99 if withdrawal.amount > 0 {
100 *balance_increments.entry(withdrawal.address).or_default() +=
101 withdrawal.amount_wei().to::<u128>();
102 }
103 }
104 }
105 }
106}
107
108pub fn balance_increment_state<DB>(
112 balance_increments: &HashMap<Address, u128>,
113 state: &mut State<DB>,
114) -> Result<EvmState, BlockExecutionError>
115where
116 DB: Database,
117{
118 let mut load_account = |address: &Address| -> Result<(Address, Account), BlockExecutionError> {
119 let cache_account = state.load_cache_account(*address).map_err(|_| {
120 BlockExecutionError::msg("could not load account for balance increment")
121 })?;
122
123 let account = cache_account.account.as_ref().ok_or_else(|| {
124 BlockExecutionError::msg("could not load account for balance increment")
125 })?;
126
127 Ok((
128 *address,
129 Account {
130 info: account.info.clone(),
131 storage: Default::default(),
132 status: AccountStatus::Touched,
133 transaction_id: 0,
134 },
135 ))
136 };
137
138 balance_increments
139 .iter()
140 .filter(|(_, &balance)| balance != 0)
141 .map(|(addr, _)| load_account(addr))
142 .collect::<Result<EvmState, _>>()
143}