From edb43827b5333e8e7d0b728c0fa6eca9e7211cec Mon Sep 17 00:00:00 2001 From: merklefruit Date: Thu, 13 Apr 2023 20:26:07 +0200 Subject: [PATCH 1/7] feat: added withdrawals_root block property --- ethers-core/src/types/block.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/ethers-core/src/types/block.rs b/ethers-core/src/types/block.rs index e43813eff..7ef006039 100644 --- a/ethers-core/src/types/block.rs +++ b/ethers-core/src/types/block.rs @@ -84,6 +84,10 @@ pub struct Block { /// Base fee per unit of gas (if past London) #[serde(rename = "baseFeePerGas")] pub base_fee_per_gas: Option, + /// Withdrawals root hash (EIP-4895) + #[serde(rename = "withdrawalsRoot")] + #[cfg(not(feature = "celo"))] + pub withdrawals_root: Option, #[cfg(feature = "celo")] #[cfg_attr(docsrs, doc(cfg(feature = "celo")))] @@ -150,9 +154,9 @@ impl Block { Ordering::Greater => { let gas_used_delta = self.gas_used - self.gas_target(); let base_fee_per_gas_delta = U256::max( - base_fee_per_gas * gas_used_delta / - target_usage / - BASE_FEE_MAX_CHANGE_DENOMINATOR, + base_fee_per_gas * gas_used_delta + / target_usage + / BASE_FEE_MAX_CHANGE_DENOMINATOR, U256::from(1u32), ); let expected_base_fee_per_gas = base_fee_per_gas + base_fee_per_gas_delta; @@ -160,9 +164,9 @@ impl Block { } Ordering::Less => { let gas_used_delta = self.gas_target() - self.gas_used; - let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta / - target_usage / - BASE_FEE_MAX_CHANGE_DENOMINATOR; + let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta + / target_usage + / BASE_FEE_MAX_CHANGE_DENOMINATOR; let expected_base_fee_per_gas = base_fee_per_gas - base_fee_per_gas_delta; Some(expected_base_fee_per_gas) } @@ -179,10 +183,10 @@ impl Block { /// [`DateTime`]. pub fn time(&self) -> Result, TimeError> { if self.timestamp.is_zero() { - return Err(TimeError::TimestampZero) + return Err(TimeError::TimestampZero); } if self.timestamp.bits() > 63 { - return Err(TimeError::TimestampOverflow) + return Err(TimeError::TimestampOverflow); } // Casting to i64 is safe because the timestamp is guaranteed to be less than 2^63. // TODO: It would be nice if there was `TryInto for U256`. @@ -218,6 +222,7 @@ impl Block { mix_hash, nonce, base_fee_per_gas, + withdrawals_root, other, .. } = self; @@ -243,6 +248,7 @@ impl Block { mix_hash, nonce, base_fee_per_gas, + withdrawals_root, transactions, other, } @@ -322,6 +328,7 @@ impl From> for Block { mix_hash, nonce, base_fee_per_gas, + withdrawals_root, other, } = full; Block { @@ -346,6 +353,7 @@ impl From> for Block { mix_hash, nonce, base_fee_per_gas, + withdrawals_root, transactions: transactions.iter().map(|tx| tx.hash).collect(), other, } @@ -502,13 +510,13 @@ impl<'de> Deserialize<'de> for BlockId { match key.as_str() { "blockNumber" => { if number.is_some() || hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockNumber")) + return Err(serde::de::Error::duplicate_field("blockNumber")); } number = Some(BlockId::Number(map.next_value::()?)) } "blockHash" => { if number.is_some() || hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockHash")) + return Err(serde::de::Error::duplicate_field("blockHash")); } hash = Some(BlockId::Hash(map.next_value::()?)) } From bba1159c0a6e7bf4150e0aa957ce5083d67fe122 Mon Sep 17 00:00:00 2001 From: merklefruit Date: Thu, 13 Apr 2023 20:31:56 +0200 Subject: [PATCH 2/7] chore: fmt --- ethers-core/src/types/block.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ethers-core/src/types/block.rs b/ethers-core/src/types/block.rs index 7ef006039..6842b5ada 100644 --- a/ethers-core/src/types/block.rs +++ b/ethers-core/src/types/block.rs @@ -154,9 +154,9 @@ impl Block { Ordering::Greater => { let gas_used_delta = self.gas_used - self.gas_target(); let base_fee_per_gas_delta = U256::max( - base_fee_per_gas * gas_used_delta - / target_usage - / BASE_FEE_MAX_CHANGE_DENOMINATOR, + base_fee_per_gas * gas_used_delta / + target_usage / + BASE_FEE_MAX_CHANGE_DENOMINATOR, U256::from(1u32), ); let expected_base_fee_per_gas = base_fee_per_gas + base_fee_per_gas_delta; @@ -164,9 +164,9 @@ impl Block { } Ordering::Less => { let gas_used_delta = self.gas_target() - self.gas_used; - let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta - / target_usage - / BASE_FEE_MAX_CHANGE_DENOMINATOR; + let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta / + target_usage / + BASE_FEE_MAX_CHANGE_DENOMINATOR; let expected_base_fee_per_gas = base_fee_per_gas - base_fee_per_gas_delta; Some(expected_base_fee_per_gas) } @@ -183,10 +183,10 @@ impl Block { /// [`DateTime`]. pub fn time(&self) -> Result, TimeError> { if self.timestamp.is_zero() { - return Err(TimeError::TimestampZero); + return Err(TimeError::TimestampZero) } if self.timestamp.bits() > 63 { - return Err(TimeError::TimestampOverflow); + return Err(TimeError::TimestampOverflow) } // Casting to i64 is safe because the timestamp is guaranteed to be less than 2^63. // TODO: It would be nice if there was `TryInto for U256`. @@ -510,13 +510,13 @@ impl<'de> Deserialize<'de> for BlockId { match key.as_str() { "blockNumber" => { if number.is_some() || hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockNumber")); + return Err(serde::de::Error::duplicate_field("blockNumber")) } number = Some(BlockId::Number(map.next_value::()?)) } "blockHash" => { if number.is_some() || hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockHash")); + return Err(serde::de::Error::duplicate_field("blockHash")) } hash = Some(BlockId::Hash(map.next_value::()?)) } From 21c5a899bae1427e026e8413c06b47b2c6437af6 Mon Sep 17 00:00:00 2001 From: merklefruit Date: Thu, 13 Apr 2023 23:55:55 +0200 Subject: [PATCH 3/7] feat: added withdrawal type --- ethers-core/src/types/block.rs | 14 +++++++++++--- ethers-core/src/types/mod.rs | 3 +++ ethers-core/src/types/withdrawal.rs | 29 +++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 ethers-core/src/types/withdrawal.rs diff --git a/ethers-core/src/types/block.rs b/ethers-core/src/types/block.rs index 6842b5ada..2f1819ac9 100644 --- a/ethers-core/src/types/block.rs +++ b/ethers-core/src/types/block.rs @@ -1,6 +1,6 @@ // Modified from -use crate::types::{Address, Bloom, Bytes, Transaction, TxHash, H256, U256, U64}; +use crate::types::{Address, Bloom, Bytes, Transaction, TxHash, Withdrawal, H256, U256, U64}; use chrono::{DateTime, TimeZone, Utc}; use serde::{ de::{MapAccess, Visitor}, @@ -84,10 +84,14 @@ pub struct Block { /// Base fee per unit of gas (if past London) #[serde(rename = "baseFeePerGas")] pub base_fee_per_gas: Option, - /// Withdrawals root hash (EIP-4895) - #[serde(rename = "withdrawalsRoot")] + /// Withdrawals root hash (if past Shanghai) + #[serde(skip_serializing_if = "Option::is_none", rename = "withdrawalsRoot")] #[cfg(not(feature = "celo"))] pub withdrawals_root: Option, + /// Withdrawals (if past Shanghai) + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg(not(feature = "celo"))] + pub withdrawals: Option>, #[cfg(feature = "celo")] #[cfg_attr(docsrs, doc(cfg(feature = "celo")))] @@ -223,6 +227,7 @@ impl Block { nonce, base_fee_per_gas, withdrawals_root, + withdrawals, other, .. } = self; @@ -249,6 +254,7 @@ impl Block { nonce, base_fee_per_gas, withdrawals_root, + withdrawals, transactions, other, } @@ -329,6 +335,7 @@ impl From> for Block { nonce, base_fee_per_gas, withdrawals_root, + withdrawals, other, } = full; Block { @@ -354,6 +361,7 @@ impl From> for Block { nonce, base_fee_per_gas, withdrawals_root, + withdrawals, transactions: transactions.iter().map(|tx| tx.hash).collect(), other, } diff --git a/ethers-core/src/types/mod.rs b/ethers-core/src/types/mod.rs index ae6a85e63..13bd610eb 100644 --- a/ethers-core/src/types/mod.rs +++ b/ethers-core/src/types/mod.rs @@ -81,3 +81,6 @@ pub use syncing::{SyncProgress, SyncingStatus}; mod opcode; pub use opcode::Opcode; + +mod withdrawal; +pub use withdrawal::Withdrawal; diff --git a/ethers-core/src/types/withdrawal.rs b/ethers-core/src/types/withdrawal.rs new file mode 100644 index 000000000..8cbb283e3 --- /dev/null +++ b/ethers-core/src/types/withdrawal.rs @@ -0,0 +1,29 @@ +use crate::types::{Address, U256, U64}; +use serde::{Deserialize, Serialize}; + +/// A validator withdrawal from the consensus layer. +/// See EIP-4895: Beacon chain push withdrawals as operations. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct Withdrawal { + /// Monotonically increasing identifier issued by consensus layer + pub index: U64, + + /// Index of validator associated with withdrawal + pub validator: U64, + + /// Target address for withdrawn ether + pub address: Address, + + /// Value of withdrawal (in wei) + pub amount: U256, +} + +impl rlp::Encodable for Withdrawal { + fn rlp_append(&self, s: &mut rlp::RlpStream) { + s.begin_list(4); + s.append(&self.index); + s.append(&self.validator); + s.append(&self.address); + s.append(&self.amount); + } +} From 65a8ba30cd8910e93ac44bcac309fee4d77e64a3 Mon Sep 17 00:00:00 2001 From: merklefruit Date: Fri, 14 Apr 2023 00:04:07 +0200 Subject: [PATCH 4/7] chore: small fixes --- ethers-core/src/types/block.rs | 24 ++++++++++++------------ ethers-core/src/types/withdrawal.rs | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ethers-core/src/types/block.rs b/ethers-core/src/types/block.rs index 2f1819ac9..f3eb6ff62 100644 --- a/ethers-core/src/types/block.rs +++ b/ethers-core/src/types/block.rs @@ -85,11 +85,11 @@ pub struct Block { #[serde(rename = "baseFeePerGas")] pub base_fee_per_gas: Option, /// Withdrawals root hash (if past Shanghai) - #[serde(skip_serializing_if = "Option::is_none", rename = "withdrawalsRoot")] + #[serde(default, skip_serializing_if = "Option::is_none", rename = "withdrawalsRoot")] #[cfg(not(feature = "celo"))] pub withdrawals_root: Option, /// Withdrawals (if past Shanghai) - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] #[cfg(not(feature = "celo"))] pub withdrawals: Option>, @@ -158,9 +158,9 @@ impl Block { Ordering::Greater => { let gas_used_delta = self.gas_used - self.gas_target(); let base_fee_per_gas_delta = U256::max( - base_fee_per_gas * gas_used_delta / - target_usage / - BASE_FEE_MAX_CHANGE_DENOMINATOR, + base_fee_per_gas * gas_used_delta + / target_usage + / BASE_FEE_MAX_CHANGE_DENOMINATOR, U256::from(1u32), ); let expected_base_fee_per_gas = base_fee_per_gas + base_fee_per_gas_delta; @@ -168,9 +168,9 @@ impl Block { } Ordering::Less => { let gas_used_delta = self.gas_target() - self.gas_used; - let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta / - target_usage / - BASE_FEE_MAX_CHANGE_DENOMINATOR; + let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta + / target_usage + / BASE_FEE_MAX_CHANGE_DENOMINATOR; let expected_base_fee_per_gas = base_fee_per_gas - base_fee_per_gas_delta; Some(expected_base_fee_per_gas) } @@ -187,10 +187,10 @@ impl Block { /// [`DateTime`]. pub fn time(&self) -> Result, TimeError> { if self.timestamp.is_zero() { - return Err(TimeError::TimestampZero) + return Err(TimeError::TimestampZero); } if self.timestamp.bits() > 63 { - return Err(TimeError::TimestampOverflow) + return Err(TimeError::TimestampOverflow); } // Casting to i64 is safe because the timestamp is guaranteed to be less than 2^63. // TODO: It would be nice if there was `TryInto for U256`. @@ -518,13 +518,13 @@ impl<'de> Deserialize<'de> for BlockId { match key.as_str() { "blockNumber" => { if number.is_some() || hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockNumber")) + return Err(serde::de::Error::duplicate_field("blockNumber")); } number = Some(BlockId::Number(map.next_value::()?)) } "blockHash" => { if number.is_some() || hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockHash")) + return Err(serde::de::Error::duplicate_field("blockHash")); } hash = Some(BlockId::Hash(map.next_value::()?)) } diff --git a/ethers-core/src/types/withdrawal.rs b/ethers-core/src/types/withdrawal.rs index 8cbb283e3..b485badce 100644 --- a/ethers-core/src/types/withdrawal.rs +++ b/ethers-core/src/types/withdrawal.rs @@ -9,6 +9,7 @@ pub struct Withdrawal { pub index: U64, /// Index of validator associated with withdrawal + #[serde(rename = "validatorIndex")] pub validator: U64, /// Target address for withdrawn ether From d3618432eee9b0794f93a1741198f1fd5fd8d524 Mon Sep 17 00:00:00 2001 From: merklefruit Date: Fri, 14 Apr 2023 00:05:09 +0200 Subject: [PATCH 5/7] chore: fmt --- ethers-core/src/types/block.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ethers-core/src/types/block.rs b/ethers-core/src/types/block.rs index f3eb6ff62..893acce9b 100644 --- a/ethers-core/src/types/block.rs +++ b/ethers-core/src/types/block.rs @@ -158,9 +158,9 @@ impl Block { Ordering::Greater => { let gas_used_delta = self.gas_used - self.gas_target(); let base_fee_per_gas_delta = U256::max( - base_fee_per_gas * gas_used_delta - / target_usage - / BASE_FEE_MAX_CHANGE_DENOMINATOR, + base_fee_per_gas * gas_used_delta / + target_usage / + BASE_FEE_MAX_CHANGE_DENOMINATOR, U256::from(1u32), ); let expected_base_fee_per_gas = base_fee_per_gas + base_fee_per_gas_delta; @@ -168,9 +168,9 @@ impl Block { } Ordering::Less => { let gas_used_delta = self.gas_target() - self.gas_used; - let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta - / target_usage - / BASE_FEE_MAX_CHANGE_DENOMINATOR; + let base_fee_per_gas_delta = base_fee_per_gas * gas_used_delta / + target_usage / + BASE_FEE_MAX_CHANGE_DENOMINATOR; let expected_base_fee_per_gas = base_fee_per_gas - base_fee_per_gas_delta; Some(expected_base_fee_per_gas) } @@ -187,10 +187,10 @@ impl Block { /// [`DateTime`]. pub fn time(&self) -> Result, TimeError> { if self.timestamp.is_zero() { - return Err(TimeError::TimestampZero); + return Err(TimeError::TimestampZero) } if self.timestamp.bits() > 63 { - return Err(TimeError::TimestampOverflow); + return Err(TimeError::TimestampOverflow) } // Casting to i64 is safe because the timestamp is guaranteed to be less than 2^63. // TODO: It would be nice if there was `TryInto for U256`. @@ -518,13 +518,13 @@ impl<'de> Deserialize<'de> for BlockId { match key.as_str() { "blockNumber" => { if number.is_some() || hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockNumber")); + return Err(serde::de::Error::duplicate_field("blockNumber")) } number = Some(BlockId::Number(map.next_value::()?)) } "blockHash" => { if number.is_some() || hash.is_some() { - return Err(serde::de::Error::duplicate_field("blockHash")); + return Err(serde::de::Error::duplicate_field("blockHash")) } hash = Some(BlockId::Hash(map.next_value::()?)) } From f64549264c50497e9bd27bac0d299405f1a1967d Mon Sep 17 00:00:00 2001 From: merklefruit Date: Fri, 14 Apr 2023 00:07:15 +0200 Subject: [PATCH 6/7] chore: renamed withdrawal field --- ethers-core/src/types/withdrawal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethers-core/src/types/withdrawal.rs b/ethers-core/src/types/withdrawal.rs index b485badce..35ed7afd2 100644 --- a/ethers-core/src/types/withdrawal.rs +++ b/ethers-core/src/types/withdrawal.rs @@ -10,7 +10,7 @@ pub struct Withdrawal { /// Index of validator associated with withdrawal #[serde(rename = "validatorIndex")] - pub validator: U64, + pub validator_index: U64, /// Target address for withdrawn ether pub address: Address, From 1a936ba3946a4fdeb8010b209fa52e8265c4e33f Mon Sep 17 00:00:00 2001 From: merklefruit Date: Fri, 14 Apr 2023 00:08:10 +0200 Subject: [PATCH 7/7] chore: small fix --- ethers-core/src/types/withdrawal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethers-core/src/types/withdrawal.rs b/ethers-core/src/types/withdrawal.rs index 35ed7afd2..b63ada3d2 100644 --- a/ethers-core/src/types/withdrawal.rs +++ b/ethers-core/src/types/withdrawal.rs @@ -23,7 +23,7 @@ impl rlp::Encodable for Withdrawal { fn rlp_append(&self, s: &mut rlp::RlpStream) { s.begin_list(4); s.append(&self.index); - s.append(&self.validator); + s.append(&self.validator_index); s.append(&self.address); s.append(&self.amount); }