1use alloc::vec::Vec;
6use alloy_primitives::{Address, U256};
7use alloy_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
8use derive_more::derive::{AsRef, Deref, DerefMut, From, IntoIterator};
9
10pub const GWEI_TO_WEI: u64 = 1_000_000_000;
12
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable)]
15#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
18#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
19pub struct Withdrawal {
20 #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
22 pub index: u64,
23 #[cfg_attr(
25 feature = "serde",
26 serde(with = "alloy_serde::quantity", rename = "validatorIndex")
27 )]
28 pub validator_index: u64,
29 pub address: Address,
31 #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
33 pub amount: u64,
34}
35
36impl Withdrawal {
37 pub fn amount_wei(&self) -> U256 {
39 U256::from(self.amount) * U256::from(GWEI_TO_WEI)
40 }
41}
42
43#[derive(
45 Debug,
46 Clone,
47 PartialEq,
48 Eq,
49 Default,
50 Hash,
51 From,
52 AsRef,
53 Deref,
54 DerefMut,
55 IntoIterator,
56 RlpEncodableWrapper,
57 RlpDecodableWrapper,
58)]
59#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
62pub struct Withdrawals(pub Vec<Withdrawal>);
63
64impl Withdrawals {
65 pub const fn new(withdrawals: Vec<Withdrawal>) -> Self {
67 Self(withdrawals)
68 }
69
70 #[inline]
72 pub const fn total_size(&self) -> usize {
73 self.0.capacity() * core::mem::size_of::<Withdrawal>()
74 }
75
76 #[inline]
78 pub const fn size(&self) -> usize {
79 self.0.len() * core::mem::size_of::<Withdrawal>()
80 }
81
82 pub fn iter(&self) -> core::slice::Iter<'_, Withdrawal> {
84 self.0.iter()
85 }
86
87 pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, Withdrawal> {
89 self.0.iter_mut()
90 }
91
92 pub fn into_inner(self) -> Vec<Withdrawal> {
94 self.0
95 }
96}
97
98impl<'a> IntoIterator for &'a Withdrawals {
99 type Item = &'a Withdrawal;
100 type IntoIter = core::slice::Iter<'a, Withdrawal>;
101 fn into_iter(self) -> Self::IntoIter {
102 self.iter()
103 }
104}
105
106impl<'a> IntoIterator for &'a mut Withdrawals {
107 type Item = &'a mut Withdrawal;
108 type IntoIter = core::slice::IterMut<'a, Withdrawal>;
109
110 fn into_iter(self) -> Self::IntoIter {
111 self.iter_mut()
112 }
113}
114
115#[cfg(all(test, feature = "serde"))]
116mod tests {
117 use super::*;
118 use core::str::FromStr;
119
120 #[test]
122 fn test_withdrawal_serde_roundtrip() {
123 let input = r#"[{"index":"0x0","validatorIndex":"0x0","address":"0x0000000000000000000000000000000000001000","amount":"0x1"},{"index":"0x1","validatorIndex":"0x1","address":"0x0000000000000000000000000000000000001001","amount":"0x1"},{"index":"0x2","validatorIndex":"0x2","address":"0x0000000000000000000000000000000000001002","amount":"0x1"},{"index":"0x3","validatorIndex":"0x3","address":"0x0000000000000000000000000000000000001003","amount":"0x1"},{"index":"0x4","validatorIndex":"0x4","address":"0x0000000000000000000000000000000000001004","amount":"0x1"},{"index":"0x5","validatorIndex":"0x5","address":"0x0000000000000000000000000000000000001005","amount":"0x1"},{"index":"0x6","validatorIndex":"0x6","address":"0x0000000000000000000000000000000000001006","amount":"0x1"},{"index":"0x7","validatorIndex":"0x7","address":"0x0000000000000000000000000000000000001007","amount":"0x1"},{"index":"0x8","validatorIndex":"0x8","address":"0x0000000000000000000000000000000000001008","amount":"0x1"},{"index":"0x9","validatorIndex":"0x9","address":"0x0000000000000000000000000000000000001009","amount":"0x1"},{"index":"0xa","validatorIndex":"0xa","address":"0x000000000000000000000000000000000000100a","amount":"0x1"},{"index":"0xb","validatorIndex":"0xb","address":"0x000000000000000000000000000000000000100b","amount":"0x1"},{"index":"0xc","validatorIndex":"0xc","address":"0x000000000000000000000000000000000000100c","amount":"0x1"},{"index":"0xd","validatorIndex":"0xd","address":"0x000000000000000000000000000000000000100d","amount":"0x1"},{"index":"0xe","validatorIndex":"0xe","address":"0x000000000000000000000000000000000000100e","amount":"0x1"},{"index":"0xf","validatorIndex":"0xf","address":"0x000000000000000000000000000000000000100f","amount":"0x1"}]"#;
124
125 let withdrawals: Vec<Withdrawal> = serde_json::from_str(input).unwrap();
127 let s = serde_json::to_string(&withdrawals).unwrap();
128 assert_eq!(input, s);
129
130 let withdrawals: Withdrawals = serde_json::from_str(input).unwrap();
132 let s = serde_json::to_string(&withdrawals).unwrap();
133 assert_eq!(input, s);
134 }
135
136 #[test]
137 fn test_withdrawal_amount_wei() {
138 let withdrawal =
139 Withdrawal { index: 1, validator_index: 2, address: Address::random(), amount: 454456 };
140
141 assert_eq!(withdrawal.amount_wei(), U256::from_str("0x19d5348723000").unwrap());
143 }
144}