1use crate::{helpers::Ctx, EvmExtUnchecked, EvmNeedsTx};
2use alloy::{
3 eips::eip4895::Withdrawal,
4 primitives::{map::HashMap, U256},
5};
6use revm::{context::result::EVMError, Database, DatabaseCommit, Inspector};
7
8impl<Db, Insp> EvmNeedsTx<Db, Insp>
9where
10 Db: Database + DatabaseCommit,
11 Insp: Inspector<Ctx<Db>>,
12{
13 pub fn apply_withdrawals<'b>(
15 &mut self,
16 withdrawals: impl IntoIterator<Item = &'b Withdrawal>,
17 ) -> Result<(), EVMError<Db::Error>> {
18 let mut changes = HashMap::default();
21
22 let increments = withdrawals
23 .into_iter()
24 .map(|withdrawal| (withdrawal.address, withdrawal.amount as u128))
25 .filter(|(_, amount)| *amount != 0);
26
27 for (address, amount) in increments {
28 let mut acct =
29 self.inner_mut_unchecked().account(address).map_err(EVMError::Database)?;
30 acct.info.balance = acct.info.balance.saturating_add(U256::from(amount));
31 acct.mark_touch();
32 changes.insert(address, acct);
33 }
34
35 self.commit_unchecked(changes);
36 Ok(())
37 }
38}
39
40#[cfg(test)]
41mod test {
42 use alloy::{
43 eips::eip4895::{Withdrawal, GWEI_TO_WEI},
44 primitives::{Address, U256},
45 };
46
47 use crate::{NoopBlock, NoopCfg};
48
49 const USER: Address = Address::with_last_byte(0x42);
50
51 const WITHDRAWALS: &[Withdrawal] =
52 &[Withdrawal { validator_index: 1, index: 1, address: USER, amount: 100 * GWEI_TO_WEI }];
53
54 #[test]
55 fn test_eip4895() {
56 let mut trevm = crate::test_utils::test_trevm().fill_cfg(&NoopCfg).fill_block(&NoopBlock);
57
58 assert_eq!(trevm.try_read_balance(USER).unwrap(), U256::ZERO);
59
60 trevm.apply_withdrawals(WITHDRAWALS).unwrap();
61
62 assert_eq!(trevm.try_read_balance(USER).unwrap(), U256::from(100 * GWEI_TO_WEI));
63 }
64}