alloy_evm/block/calc.rs
1//! Helpers to perform common calculations.
2
3use alloy_consensus::constants::ETH_TO_WEI;
4use alloy_hardforks::EthereumHardforks;
5use alloy_primitives::BlockNumber;
6
7/// Calculates the base block reward.
8///
9/// The base block reward is defined as:
10///
11/// - For Paris and later: `None`
12/// - For Petersburg and later: `Some(2 ETH)`
13/// - For Byzantium and later: `Some(3 ETH)`
14/// - Otherwise: `Some(5 ETH)`
15///
16/// # Note
17///
18/// This does not include the reward for including ommers. To calculate the full block reward, see
19/// [`block_reward`].
20///
21/// # References
22///
23/// - Definition: [Yellow Paper][yp] (page 15, 11.3)
24///
25/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
26pub fn base_block_reward(spec: impl EthereumHardforks, block_number: BlockNumber) -> Option<u128> {
27 if spec.is_paris_active_at_block(block_number) {
28 None
29 } else {
30 Some(base_block_reward_pre_merge(spec, block_number))
31 }
32}
33
34/// Calculates the base block reward __before__ the merge (Paris hardfork).
35///
36/// Caution: The caller must ensure that the block number is before the merge.
37pub fn base_block_reward_pre_merge(
38 spec: impl EthereumHardforks,
39 block_number: BlockNumber,
40) -> u128 {
41 if spec.is_constantinople_active_at_block(block_number) {
42 ETH_TO_WEI * 2
43 } else if spec.is_byzantium_active_at_block(block_number) {
44 ETH_TO_WEI * 3
45 } else {
46 ETH_TO_WEI * 5
47 }
48}
49
50/// Calculates the reward for a block, including the reward for ommer inclusion.
51///
52/// The base reward should be calculated using [`base_block_reward`]. `ommers` represents the number
53/// of ommers included in the block.
54///
55/// # Examples
56///
57/// ```
58/// # use alloy_hardforks::EthereumChainHardforks;
59/// # use alloy_evm::block::calc::{base_block_reward, block_reward};
60/// # use alloy_consensus::constants::ETH_TO_WEI;
61/// # use alloy_primitives::U256;
62/// #
63/// // This is block 126 on mainnet.
64/// let block_number = 126;
65/// let number_of_ommers = 1;
66///
67/// let reward = base_block_reward(EthereumChainHardforks::mainnet(), block_number)
68/// .map(|reward| block_reward(reward, 1));
69///
70/// // The base block reward is 5 ETH, and the ommer inclusion reward is 1/32th of 5 ETH.
71/// assert_eq!(reward.unwrap(), ETH_TO_WEI * 5 + ((ETH_TO_WEI * 5) >> 5));
72/// ```
73///
74/// # References
75///
76/// - Definition: [Yellow Paper][yp] (page 15, 11.3)
77///
78/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
79pub const fn block_reward(base_block_reward: u128, ommers: usize) -> u128 {
80 base_block_reward + (base_block_reward >> 5) * ommers as u128
81}
82
83/// Calculate the reward for an ommer.
84///
85/// # Application
86///
87/// Rewards are accumulative, so they should be added to the beneficiary addresses in addition to
88/// any other rewards from the same block.
89///
90/// From the yellow paper (page 15):
91///
92/// > If there are collisions of the beneficiary addresses between ommers and the block (i.e. two
93/// > ommers with the same beneficiary address or an ommer with the same beneficiary address as the
94/// > present block), additions are applied cumulatively.
95///
96/// # References
97///
98/// - Implementation: [OpenEthereum][oe]
99/// - Definition: [Yellow Paper][yp] (page 15, 11.3)
100///
101/// [oe]: https://github.com/openethereum/openethereum/blob/6c2d392d867b058ff867c4373e40850ca3f96969/crates/ethcore/src/ethereum/ethash.rs#L319-L333
102/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
103pub const fn ommer_reward(
104 base_block_reward: u128,
105 block_number: BlockNumber,
106 ommer_block_number: BlockNumber,
107) -> u128 {
108 ((8 + ommer_block_number - block_number) as u128 * base_block_reward) >> 3
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use alloy_hardforks::EthereumChainHardforks;
115 use alloy_primitives::U256;
116
117 #[test]
118 fn calc_base_block_reward() {
119 // ((block number, td), reward)
120 let cases = [
121 // Pre-byzantium
122 ((0, U256::ZERO), Some(ETH_TO_WEI * 5)),
123 // Byzantium
124 ((4370000, U256::ZERO), Some(ETH_TO_WEI * 3)),
125 // Petersburg
126 ((7280000, U256::ZERO), Some(ETH_TO_WEI * 2)),
127 // Merge
128 ((15537394, U256::from(58_750_000_000_000_000_000_000_u128)), None),
129 ];
130
131 for ((block_number, _td), expected_reward) in cases {
132 assert_eq!(
133 base_block_reward(EthereumChainHardforks::mainnet(), block_number),
134 expected_reward
135 );
136 }
137 }
138
139 #[test]
140 fn calc_full_block_reward() {
141 let base_reward = ETH_TO_WEI;
142 let one_thirty_twoth_reward = base_reward >> 5;
143
144 // (num_ommers, reward)
145 let cases = [
146 (0, base_reward),
147 (1, base_reward + one_thirty_twoth_reward),
148 (2, base_reward + one_thirty_twoth_reward * 2),
149 ];
150
151 for (num_ommers, expected_reward) in cases {
152 assert_eq!(block_reward(base_reward, num_ommers), expected_reward);
153 }
154 }
155}