diff --git a/crates/eips/Cargo.toml b/crates/eips/Cargo.toml index 93f250026e4..6c55582a7d8 100644 --- a/crates/eips/Cargo.toml +++ b/crates/eips/Cargo.toml @@ -36,7 +36,9 @@ derive_more = { workspace = true, features = [ "as_ref", "deref", "deref_mut", -], default-features = false, optional = true } + "from", + "into_iterator", +], default-features = false } once_cell = { workspace = true, features = ["race", "alloc"], optional = true } sha2 = { workspace = true, optional = true } @@ -60,12 +62,12 @@ rand.workspace = true [features] default = ["std", "kzg-sidecar"] -std = ["alloy-primitives/std", "alloy-rlp/std", "derive_more?/std", +std = ["alloy-primitives/std", "alloy-rlp/std", "serde?/std", "c-kzg?/std", "once_cell?/std"] -serde = ["dep:alloy-serde", "dep:serde", "alloy-primitives/serde", +serde = ["dep:alloy-serde", "dep:serde", "alloy-primitives/serde", "c-kzg?/serde", "alloy-eip2930/serde", "alloy-eip7702/serde"] serde-bincode-compat = ["alloy-eip7702/serde-bincode-compat"] -kzg = ["kzg-sidecar", "sha2", "dep:derive_more", "dep:c-kzg", "dep:once_cell"] +kzg = ["kzg-sidecar", "sha2", "dep:c-kzg", "dep:once_cell"] kzg-sidecar = ["sha2"] k256 = ["alloy-eip7702/k256"] sha2 = ["dep:sha2"] diff --git a/crates/eips/src/eip4895.rs b/crates/eips/src/eip4895.rs index 61a2e7049c7..460fcaa5ce6 100644 --- a/crates/eips/src/eip4895.rs +++ b/crates/eips/src/eip4895.rs @@ -2,8 +2,10 @@ //! //! [EIP-4895]: https://eips.ethereum.org/EIPS/eip-4895 +use alloc::vec::Vec; use alloy_primitives::{Address, U256}; -use alloy_rlp::{RlpDecodable, RlpEncodable}; +use alloy_rlp::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; +use derive_more::derive::{AsRef, Deref, DerefMut, From, IntoIterator}; /// Multiplier for converting gwei to wei. pub const GWEI_TO_WEI: u64 = 1_000_000_000; @@ -37,6 +39,77 @@ impl Withdrawal { } } +/// Represents a collection of Withdrawals. +#[derive( + Debug, + Clone, + PartialEq, + Eq, + Default, + Hash, + From, + AsRef, + Deref, + DerefMut, + IntoIterator, + RlpEncodableWrapper, + RlpDecodableWrapper, +)] +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Withdrawals(Vec); + +impl Withdrawals { + /// Create a new Withdrawals instance. + pub const fn new(withdrawals: Vec) -> Self { + Self(withdrawals) + } + + /// Calculate the total size, including capacity, of the Withdrawals. + #[inline] + pub fn total_size(&self) -> usize { + self.0.capacity() * core::mem::size_of::() + } + + /// Calculate a heuristic for the in-memory size of the [Withdrawals]. + #[inline] + pub fn size(&self) -> usize { + self.0.len() * core::mem::size_of::() + } + + /// Get an iterator over the Withdrawals. + pub fn iter(&self) -> core::slice::Iter<'_, Withdrawal> { + self.0.iter() + } + + /// Get a mutable iterator over the Withdrawals. + pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, Withdrawal> { + self.0.iter_mut() + } + + /// Convert [Self] into raw vec of withdrawals. + pub fn into_inner(self) -> Vec { + self.0 + } +} + +impl<'a> IntoIterator for &'a Withdrawals { + type Item = &'a Withdrawal; + type IntoIter = core::slice::Iter<'a, Withdrawal>; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a> IntoIterator for &'a mut Withdrawals { + type Item = &'a mut Withdrawal; + type IntoIter = core::slice::IterMut<'a, Withdrawal>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + #[cfg(all(test, feature = "serde"))] mod tests { use super::*; @@ -46,8 +119,14 @@ mod tests { fn test_withdrawal_serde_roundtrip() { 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"}]"#; + // With a vector of withdrawals. let withdrawals: Vec = serde_json::from_str(input).unwrap(); let s = serde_json::to_string(&withdrawals).unwrap(); assert_eq!(input, s); + + // With a Withdrawals struct. + let withdrawals: Withdrawals = serde_json::from_str(input).unwrap(); + let s = serde_json::to_string(&withdrawals).unwrap(); + assert_eq!(input, s); } }