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}